Compare commits

..

11 Commits

Author SHA1 Message Date
Benjamin Doherty
cb8914ab96 Merge branch 'rc/1.25.4' into release 2022-08-02 11:47:52 -07:00
Alan Eneev
2fecda7bdc Headless EGL: Fallback to a 24-bit depth buffer 2022-07-29 10:10:46 -07:00
Alan Eneev
543d8efb25 Fix PlatformEGLHeadless build and add a build.sh option to build EGL
I have disabled building SDL with headless EGL, because
SDL_config_minimal.h doesn't work with EGL, and I don't know how to
implement a an SDL config that would work with EGL.

I have verified that this works in a separate project, and that it
compiles in this project.

```
$ ./build.sh -e release
$ find ./out/ -name "*EGL*"
./out/cmake-release/filament/backend/CMakeFiles/backend.dir/src/opengl/platforms/PlatformEGL.cpp.o
./out/cmake-release/filament/backend/CMakeFiles/backend.dir/src/opengl/platforms/PlatformEGLHeadless.cpp.o
./out/cmake-release/libs/bluegl/CMakeFiles/bluegl.dir/src/BlueGLLinuxEGL.cpp.o
```
2022-07-29 10:10:40 -07:00
Benjamin Doherty
396b1079a7 Bump version to 1.25.4 2022-07-26 09:57:34 -07:00
Benjamin Doherty
90f508e89d Release Filament 1.25.3 2022-07-26 09:55:54 -07:00
Ben Doherty
d70cee7fec Fix config object related build failures (#5814) 2022-07-26 09:52:19 -07:00
Mathias Agopian
9c542791ef fix max mipmap levels calculation for 3D textures
the depth of the texture wasn't taken into account. In practice this
would restrict the number of mip levels of a 3d texture that would have
a larger dimension in depth.
2022-07-25 11:30:27 -07:00
Mathias Agopian
2d1d6ffb2c fix cubemap uploading
There was a wrong assert in Texture::setImage() as well as a typo when
calling setImage() itself in the sample code.
2022-07-25 11:30:08 -07:00
Romain Guy
9cc7fe97c5 Upgrade Kotlin, AGP, NDK (#5804) 2022-07-22 13:46:13 -07:00
Mathias Agopian
61fb5a588e rework backend texture upload APIs
The main goal is to allow more flexibility, allow cubemap arrays in
the future and better match vk and metal apis.

Main changes:
- remove updateCubeImage
- remove update2DImage
- update3DImage is now the only texture upload backend API

cubemaps are now treated just like a 2D array of 6 layers.

For this reason, Texture::setImage(..., FaceOFfsets) is deprecated.
Additionally, the 2D versions of Texture::setImage() become inline
helpers.

A side effect of this change is that it is now possible to update only
a single face of a cubemap, but also a region of a face (or faces).
2022-07-21 11:12:05 -07:00
Ben Doherty
815d202f6d Use staging buffers to implement Metal buffer updates (#5795)
This PR changes how Metal handles buffer updates. Previously, Metal allocated a full new buffer each time an `updateBufferObject` command was issued; however, it did not copy the previous contents of the buffer over to the new buffer, so partial updates did not work correctly.

Now, Metal allocates a single, private GPU buffer and employs temporary staging buffers whose contents get blitted to the private buffer at each update.

There's still some room for optimizations, and I need to give more thought to how I want to implement `updateBufferObjectUnsynchronized`.
2022-07-18 18:08:00 -07:00
49 changed files with 498 additions and 733 deletions

View File

@@ -699,7 +699,9 @@ if (IS_HOST_PLATFORM)
if (FILAMENT_SUPPORTS_OPENGL)
add_subdirectory(${LIBRARIES}/bluegl)
endif()
add_subdirectory(${LIBRARIES}/filamentapp)
if (NOT FILAMENT_SKIP_SDL2)
add_subdirectory(${LIBRARIES}/filamentapp)
endif()
add_subdirectory(${LIBRARIES}/imageio)
add_subdirectory(${FILAMENT}/samples)

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.25.3'
implementation 'com.google.android.filament:filament-android:1.25.4'
}
```
@@ -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:
```
pod 'Filament', '~> 1.25.3'
pod 'Filament', '~> 1.25.4'
```
### Snapshots

View File

@@ -5,6 +5,10 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.25.4
- backend: streamline texture upload APIs [⚠️ **API Change**]
## v1.25.3
- engine: Fix Adreno gpu crash introduced by gpu morph target change

View File

@@ -81,10 +81,10 @@ buildscript {
'minSdk': 19,
'targetSdk': 31,
'compileSdk': 31,
'kotlin': '1.6.21',
'kotlin': '1.7.10',
'kotlin_coroutines': '1.6.1',
'buildTools': '32.0.0',
'ndk': '24.0.8215888'
'buildTools': '33.0.0',
'ndk': '25.0.8775105'
]
ext.deps = [
@@ -101,7 +101,7 @@ buildscript {
dependencies {
// NOTE: See TODO in gradle.properties once we move to Gradle 7.4
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}

View File

@@ -188,69 +188,6 @@ Java_com_google_android_filament_Texture_nGetInternalFormat(JNIEnv*, jclass,
return (jint) texture->getFormat();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImage(JNIEnv* env, jclass, jlong nativeTexture,
jlong nativeEngine, jint level, jint xoffset, jint yoffset, jint width, jint height,
jobject storage, jint remaining,
jint left, jint top, jint type, jint alignment,
jint stride, jint format,
jobject handler, jobject runnable) {
Texture* texture = (Texture*) nativeTexture;
Engine* engine = (Engine*) nativeEngine;
size_t sizeInBytes = getTextureDataSize(texture, (size_t) level, (Texture::Format) format,
(Texture::Type) type, (size_t) stride, (size_t) height, (size_t) alignment);
AutoBuffer nioBuffer(env, storage, 0);
if (sizeInBytes > (size_t(remaining) << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
void *buffer = nioBuffer.getData();
auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
Texture::PixelBufferDescriptor desc(buffer, sizeInBytes, (backend::PixelDataFormat) format,
(backend::PixelDataType) type, (uint8_t) alignment, (uint32_t) left, (uint32_t) top,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level, (uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height, std::move(desc));
return 0;
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImageCompressed(JNIEnv *env, jclass,
jlong nativeTexture, jlong nativeEngine, jint level, jint xoffset, jint yoffset,
jint width, jint height, jobject storage, jint remaining,
jint, jint, jint, jint, jint compressedSizeInBytes, jint compressedFormat,
jobject handler, jobject runnable) {
Texture *texture = (Texture *) nativeTexture;
Engine *engine = (Engine *) nativeEngine;
size_t sizeInBytes = (size_t) compressedSizeInBytes;
AutoBuffer nioBuffer(env, storage, 0);
if (sizeInBytes > (size_t(remaining) << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
void *buffer = nioBuffer.getData();
auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
Texture::PixelBufferDescriptor desc(buffer, sizeInBytes,
(backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
texture->setImage(*engine, (size_t) level, (uint32_t) xoffset, (uint32_t) yoffset,
(uint32_t) width, (uint32_t) height, std::move(desc));
return 0;
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nSetImage3D(JNIEnv* env, jclass, jlong nativeTexture,
jlong nativeEngine, jint level,
@@ -353,7 +290,10 @@ Java_com_google_android_filament_Texture_nSetImageCubemap(JNIEnv *env, jclass,
(uint32_t) stride,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop
return 0;
}
@@ -388,7 +328,10 @@ Java_com_google_android_filament_Texture_nSetImageCubemapCompressed(JNIEnv *env,
(backend::CompressedPixelDataType) compressedFormat, (uint32_t) compressedSizeInBytes,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, callback);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
texture->setImage(*engine, (size_t) level, std::move(desc), faceOffsets);
#pragma clang diagnostic pop
return 0;
}

View File

@@ -912,7 +912,7 @@ public class Texture {
public void setImage(@NonNull Engine engine,
@IntRange(from = 0) int level,
@NonNull PixelBufferDescriptor buffer) {
setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer);
setImage(engine, level, 0, 0, 0, getWidth(level), getHeight(level), 1, buffer);
}
@@ -947,25 +947,7 @@ public class Texture {
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@IntRange(from = 0) int width, @IntRange(from = 0) int height,
@NonNull PixelBufferDescriptor buffer) {
int result;
if (buffer.type == COMPRESSED) {
result = nSetImageCompressed(getNativeObject(), engine.getNativeObject(), level,
xoffset, yoffset, width, height,
buffer.storage, buffer.storage.remaining(),
buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
buffer.compressedSizeInBytes, buffer.compressedFormat.ordinal(),
buffer.handler, buffer.callback);
} else {
result = nSetImage(getNativeObject(), engine.getNativeObject(), level,
xoffset, yoffset, width, height,
buffer.storage, buffer.storage.remaining(),
buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
buffer.stride, buffer.format.ordinal(),
buffer.handler, buffer.callback);
}
if (result < 0) {
throw new BufferOverflowException();
}
setImage(engine, level, xoffset, yoffset, 0, width, height, 1, buffer);
}
/**
@@ -1046,7 +1028,9 @@ public class Texture {
*
* @see Builder#sampler
* @see PixelBufferDescriptor
* @deprecated use {@link #setImage(Engine, int, int, int, int, int, int, int, PixelBufferDescriptor)}
*/
@Deprecated
public void setImage(@NonNull Engine engine, @IntRange(from = 0) int level,
@NonNull PixelBufferDescriptor buffer,
@NonNull @Size(min = 6) int[] faceOffsetsInBytes) {
@@ -1258,18 +1242,6 @@ public class Texture {
private static native int nGetTarget(long nativeTexture);
private static native int nGetInternalFormat(long nativeTexture);
private static native int nSetImage(long nativeTexture, long nativeEngine,
int level, int xoffset, int yoffset, int width, int height,
Buffer storage, int remaining, int left, int top, int type, int alignment,
int stride, int format,
Object handler, Runnable callback);
private static native int nSetImageCompressed(long nativeTexture, long nativeEngine,
int level, int xoffset, int yoffset, int width, int height,
Buffer storage, int remaining, int left, int top, int type, int alignment,
int compressedSizeInBytes, int compressedFormat,
Object handler, Runnable callback);
private static native int nSetImage3D(long nativeTexture, long nativeEngine,
int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth,
Buffer storage, int remaining, int left, int top, int type, int alignment,

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.25.3
VERSION_NAME=1.25.4
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -44,6 +44,8 @@ function print_help {
echo " Add iOS simulator support to the iOS build."
echo " -t"
echo " Enable SwiftShader support for Vulkan in desktop builds."
echo " -e"
echo " Enable EGL on Linux support for desktop builds."
echo " -l"
echo " Build arm64/x86_64 universal libraries."
echo " For iOS, this builds universal binaries for devices and the simulator (implies -s)."
@@ -155,6 +157,8 @@ VULKAN_ANDROID_GRADLE_OPTION=""
SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=OFF"
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
@@ -215,6 +219,7 @@ function build_desktop_target {
-DCMAKE_BUILD_TYPE="$1" \
-DCMAKE_INSTALL_PREFIX="../${lc_target}/filament" \
${SWIFTSHADER_OPTION} \
${EGL_ON_LINUX_OPTION} \
${MATDBG_OPTION} \
${deployment_target} \
${architectures} \
@@ -735,7 +740,7 @@ function run_tests {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfijmp:q:uvslwtdk:" opt; do
while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
case ${opt} in
h)
print_help
@@ -842,6 +847,10 @@ while getopts ":hacCfijmp:q:uvslwtdk:" opt; do
SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=ON"
echo "SwiftShader support enabled."
;;
e)
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=ON -DFILAMENT_SKIP_SDL2=ON -DFILAMENT_SKIP_SAMPLES=ON"
echo "EGL on Linux support enabled; skipping SLD2."
;;
l)
IOS_BUILD_SIMULATOR=true
BUILD_UNIVERSAL_LIBRARIES=true

View File

@@ -1 +1 @@
24.0.8215888
25.0.8775105

View File

@@ -642,50 +642,6 @@ inline constexpr int operator +(TextureCubemapFace rhs) noexcept {
return int(rhs);
}
//! Face offsets for all faces of a cubemap
struct FaceOffsets {
using size_type = size_t;
union {
struct {
size_type px; //!< +x face offset in bytes
size_type nx; //!< -x face offset in bytes
size_type py; //!< +y face offset in bytes
size_type ny; //!< -y face offset in bytes
size_type pz; //!< +z face offset in bytes
size_type nz; //!< -z face offset in bytes
};
size_type offsets[6];
};
size_type operator[](size_t n) const noexcept { return offsets[n]; }
size_type& operator[](size_t n) { return offsets[n]; }
FaceOffsets() noexcept = default;
explicit FaceOffsets(size_type faceSize) noexcept {
px = faceSize * 0;
nx = faceSize * 1;
py = faceSize * 2;
ny = faceSize * 3;
pz = faceSize * 4;
nz = faceSize * 5;
}
FaceOffsets(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
}
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
return *this;
}
};
//! Sampler Wrap mode
enum class SamplerWrapMode : uint8_t {
CLAMP_TO_EDGE, //!< clamp-to-edge. The edge of the texture extends to infinity.
@@ -1063,7 +1019,6 @@ utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::Textu
utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::BufferObjectBinding binding);
utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::TextureSwizzle swizzle);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::AttributeArray& type);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::FaceOffsets& type);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PolygonOffset& po);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RasterState& rs);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RenderPassParams& b);

View File

@@ -337,15 +337,6 @@ DECL_DRIVER_API_N(updateSamplerGroup,
backend::SamplerGroupHandle, ubh,
backend::SamplerGroup&&, samplerGroup)
DECL_DRIVER_API_N(update2DImage,
backend::TextureHandle, th,
uint32_t, level,
uint32_t, xoffset,
uint32_t, yoffset,
uint32_t, width,
uint32_t, height,
backend::PixelBufferDescriptor&&, data)
DECL_DRIVER_API_N(setMinMaxLevels,
backend::TextureHandle, th,
uint32_t, minLevel,
@@ -362,12 +353,6 @@ DECL_DRIVER_API_N(update3DImage,
uint32_t, depth,
backend::PixelBufferDescriptor&&, data)
DECL_DRIVER_API_N(updateCubeImage,
backend::TextureHandle, th,
uint32_t, level,
backend::PixelBufferDescriptor&&, data,
backend::FaceOffsets, faceOffsets)
DECL_DRIVER_API_N(generateMipmaps,
backend::TextureHandle, th)

View File

@@ -52,9 +52,6 @@ public:
* @return The MTLBuffer representing the current state of the buffer to bind, or nil if there
* is no device allocation.
*
* For STREAM buffers, getGpuBufferStreamOffset() should be called to retrieve the correct
* buffer offset.
*
*/
id<MTLBuffer> getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) noexcept;
@@ -77,9 +74,8 @@ public:
private:
BufferUsage mUsage;
id<MTLBuffer> mBuffer = nil;
size_t mBufferSize = 0;
const MetalBufferPoolEntry* mBufferPoolEntry = nullptr;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
};

View File

@@ -16,9 +16,7 @@
#include "MetalBuffer.h"
#include <utils/Panic.h>
#include <array>
#include "MetalContext.h"
namespace filament {
namespace backend {
@@ -27,25 +25,21 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferUsage usage, size_t size,
: mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
if (size <= 4 * 1024) {
if (usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBufferPoolEntry = nullptr;
mCpuBuffer = malloc(size);
}
}
if (size <= 4 * 1024 && usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
mUsage = usage;
// Otherwise, we allocate a private GPU buffer.
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
}
MetalBuffer::~MetalBuffer() {
if (mCpuBuffer) {
free(mCpuBuffer);
}
// This buffer is being destroyed. If we have a buffer pool entry, release it as it is no longer
// needed.
if (mBufferPoolEntry) {
mContext.bufferPool->releaseBuffer(mBufferPoolEntry);
}
}
void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
@@ -53,7 +47,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
return;
}
ASSERT_PRECONDITION(size + byteOffset <= mBufferSize,
"Attempting to copy %d bytes into a buffer of size %d at offset %d",
"Attempting to copy %zu bytes into a buffer of size %zu at offset %zu",
size, mBufferSize, byteOffset);
// Either copy into the Metal buffer or into our cpu buffer.
@@ -62,14 +56,26 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
return;
}
// We're about to acquire a new buffer to hold the new contents. If we previously had obtained a
// buffer we release it, decrementing its reference count, as we no longer needs it.
if (mBufferPoolEntry) {
mContext.bufferPool->releaseBuffer(mBufferPoolEntry);
}
// Acquire a staging buffer to hold the contents of this update.
MetalBufferPool* bufferPool = mContext.bufferPool;
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
memcpy(staging->buffer.contents, src, size);
mBufferPoolEntry = mContext.bufferPool->acquireBuffer(mBufferSize);
memcpy(static_cast<uint8_t*>(mBufferPoolEntry->buffer.contents) + byteOffset, src, size);
// The blit below requires that byteOffset be a multiple of 4.
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
// Encode a blit from the staging buffer into the private GPU buffer.
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
[blitEncoder copyFromBuffer:staging->buffer
sourceOffset:0
toBuffer:mBuffer
destinationOffset:byteOffset
size:size];
[blitEncoder endEncoding];
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
bufferPool->releaseBuffer(staging);
}];
}
void MetalBuffer::copyIntoBufferUnsynchronized(void* src, size_t size, size_t byteOffset) {
@@ -78,31 +84,13 @@ void MetalBuffer::copyIntoBufferUnsynchronized(void* src, size_t size, size_t by
}
id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) noexcept {
if (!mBufferPoolEntry) {
// If there's a CPU buffer, then we return nil here, as the CPU-side buffer will be bound
// separately.
if (mCpuBuffer) {
return nil;
}
// If there isn't a CPU buffer, it means no data has been loaded into this buffer yet. To
// avoid an error, we'll allocate an empty buffer.
mBufferPoolEntry = mContext.bufferPool->acquireBuffer(mBufferSize);
// If there's a CPU buffer, then we return nil here, as the CPU-side buffer will be bound
// separately.
if (mCpuBuffer) {
return nil;
}
// This buffer is being used in a draw call, so we retain it so it's not released back into the
// buffer pool until the frame has finished.
auto uniformDeleter = [bufferPool = mContext.bufferPool] (const void* resource) {
bufferPool->releaseBuffer((const MetalBufferPoolEntry*) resource);
};
if (mContext.resourceTracker.trackResource((__bridge void*) cmdBuffer, mBufferPoolEntry,
uniformDeleter)) {
// We only want to retain the buffer once per command buffer- trackResource will return
// true if this is the first time tracking this uniform for this command buffer.
mContext.bufferPool->retainBuffer(mBufferPoolEntry);
}
return mBufferPoolEntry->buffer;
assert_invariant(mBuffer);
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,

View File

@@ -43,6 +43,7 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
// We were not able to find a sufficiently large stage, so create a new one.
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
options:MTLResourceStorageModeShared];
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({
.buffer = buffer,
.capacity = numBytes,

View File

@@ -703,6 +703,8 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
uint32_t byteOffset) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"updateBufferObject must be called outside of a render pass.");
auto* bo = handle_cast<MetalBufferObject>(boh);
bo->updateBuffer(data.buffer, data.size, byteOffset);
scheduleDestroy(std::move(data));
@@ -732,15 +734,6 @@ void MetalDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t ind
vertexBuffer->buffers[index] = bufferObject->getBuffer();
}
void MetalDriver::update2DImage(Handle<HwTexture> th, uint32_t level, uint32_t xoffset,
uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"update2DImage must be called outside of a render pass.");
auto tex = handle_cast<MetalTexture>(th);
tex->loadImage(level, MTLRegionMake2D(xoffset, yoffset, width, height), data);
scheduleDestroy(std::move(data));
}
void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
}
@@ -755,15 +748,6 @@ void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
scheduleDestroy(std::move(data));
}
void MetalDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"updateCubeImage must be called outside of a render pass.");
auto tex = handle_cast<MetalTexture>(th);
tex->loadCubeImage(faceOffsets, level, data);
scheduleDestroy(std::move(data));
}
void MetalDriver::setupExternalImage(void* image) {
// This is called when passing in a CVPixelBuffer as either an external image or swap chain.
// Here we take ownership of the passed in buffer. It will be released the next time

View File

@@ -198,12 +198,11 @@ struct MetalTexture : public HwTexture {
~MetalTexture();
void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept;
void loadCubeImage(const FaceOffsets& faceOffsets, int miplevel, PixelBufferDescriptor& p);
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor& data,
PixelBufferDescriptor const& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor& data,
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void updateLodRange(uint32_t level);

View File

@@ -641,6 +641,7 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
break;
}
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_2D_ARRAY: {
// Metal uses 'slice' (not z offset) to index into individual layers of a texture array.
const uint32_t slice = region.origin.z;
@@ -659,7 +660,6 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
break;
}
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_EXTERNAL: {
assert_invariant(false);
}
@@ -668,26 +668,8 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
updateLodRange(level);
}
void MetalTexture::loadCubeImage(const FaceOffsets& faceOffsets, int miplevel,
PixelBufferDescriptor& p) {
PixelBufferDescriptor* data = &p;
PixelBufferDescriptor reshapedData;
if(reshape(p, reshapedData)) {
data = &reshapedData;
}
const NSUInteger faceWidth = width >> miplevel;
for (NSUInteger slice = 0; slice < 6; slice++) {
FaceOffsets::size_type faceOffset = faceOffsets.offsets[slice];
loadSlice(miplevel, MTLRegionMake2D(0, 0, faceWidth, faceWidth), faceOffset, slice, *data);
}
updateLodRange((uint32_t) miplevel);
}
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor& data) noexcept {
PixelBufferDescriptor const& data) noexcept {
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
ASSERT_PRECONDITION(data.size >= shape.totalBytes,
@@ -722,7 +704,7 @@ void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffs
}
void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region,
PixelBufferDescriptor& data, const PixelBufferShape& shape) {
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.contents,
@@ -750,7 +732,7 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
}
void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region,
PixelBufferDescriptor& data, const PixelBufferShape& shape) {
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
MTLPixelFormat stagingPixelFormat = getMetalFormat(data.format, data.type);
MTLTextureDescriptor* descriptor = [MTLTextureDescriptor new];
descriptor.textureType = region.size.depth == 1 ? MTLTextureType2D : MTLTextureType3D;

View File

@@ -207,12 +207,6 @@ void NoopDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t inde
Handle<HwBufferObject> boh) {
}
void NoopDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
scheduleDestroy(std::move(data));
}
void NoopDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
}
@@ -223,11 +217,6 @@ void NoopDriver::update3DImage(Handle<HwTexture> th,
scheduleDestroy(std::move(data));
}
void NoopDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
scheduleDestroy(std::move(data));
}
void NoopDriver::setupExternalImage(void* image) {
}

View File

@@ -1753,21 +1753,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
*sb->sb = std::move(samplerGroup); // NOLINT(performance-move-const-arg)
}
void OpenGLDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture *>(th);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t,
level, xoffset, yoffset, 0, width, height, 1, std::move(data), nullptr);
} else {
setTextureData(t,
level, xoffset, yoffset, 0, width, height, 1, std::move(data), nullptr);
}
}
void OpenGLDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -1795,24 +1780,10 @@ void OpenGLDriver::update3DImage(Handle<HwTexture> th,
GLTexture* t = handle_cast<GLTexture *>(th);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t,
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data), nullptr);
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data));
} else {
setTextureData(t,
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data), nullptr);
}
}
void OpenGLDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture *>(th);
auto width = std::max(1u, t->width >> level);
auto height = std::max(1u, t->height >> level);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
} else {
setTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data));
}
}
@@ -1842,11 +1813,10 @@ bool OpenGLDriver::canGenerateMipmaps() {
return true;
}
void OpenGLDriver::setTextureData(GLTexture* t,
uint32_t level,
void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p, FaceOffsets const* faceOffsets) {
PixelBufferDescriptor&& p) {
auto& gl = mContext;
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
@@ -1861,10 +1831,10 @@ void OpenGLDriver::setTextureData(GLTexture* t,
GLenum glFormat = getFormat(p.format);
GLenum glType = getType(p.type);
gl.pixelStore(GL_UNPACK_ROW_LENGTH, p.stride);
gl.pixelStore(GL_UNPACK_ALIGNMENT, p.alignment);
gl.pixelStore(GL_UNPACK_SKIP_PIXELS, p.left);
gl.pixelStore(GL_UNPACK_SKIP_ROWS, p.top);
gl.pixelStore(GL_UNPACK_ROW_LENGTH, GLint(p.stride));
gl.pixelStore(GL_UNPACK_ALIGNMENT, GLint(p.alignment));
gl.pixelStore(GL_UNPACK_SKIP_PIXELS, GLint(p.left));
gl.pixelStore(GL_UNPACK_SKIP_ROWS, GLint(p.top));
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
@@ -1878,7 +1848,7 @@ void OpenGLDriver::setTextureData(GLTexture* t,
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
width, height, glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_3D:
assert_invariant(zoffset + depth <= std::max(1u, t->depth >> level));
@@ -1887,7 +1857,7 @@ void OpenGLDriver::setTextureData(GLTexture* t,
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
width, height, depth, glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
assert_invariant(zoffset + depth <= t->depth);
@@ -1897,19 +1867,23 @@ void OpenGLDriver::setTextureData(GLTexture* t,
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
width, height, depth, glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_CUBEMAP: {
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
FaceOffsets const& offsets = *faceOffsets;
assert_invariant(width == height);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
p.format, p.type, p.stride ? p.stride : width, height, p.alignment);
assert_invariant(zoffset + depth <= 6);
UTILS_NOUNROLL
for (size_t face = 0; face < 6; face++) {
GLenum target = getCubemapTarget(face);
glTexSubImage2D(target, GLint(level), 0, 0,
width, height, glFormat, glType,
static_cast<uint8_t const*>(p.buffer) + offsets[face]);
for (size_t face = 0; face < depth; face++) {
GLenum target = getCubemapTarget(zoffset + face);
glTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), glFormat, glType,
static_cast<uint8_t const*>(p.buffer) + faceSize * face);
}
break;
}
@@ -1932,10 +1906,10 @@ void OpenGLDriver::setTextureData(GLTexture* t,
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p, FaceOffsets const* faceOffsets) {
PixelBufferDescriptor&& p) {
auto& gl = mContext;
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
@@ -1966,7 +1940,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glCompressedTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
width, height, t->gl.internalFormat, imageSize, p.buffer);
GLsizei(width), GLsizei(height),
t->gl.internalFormat, imageSize, p.buffer);
break;
case SamplerType::SAMPLER_3D:
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
@@ -1974,26 +1949,31 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glCompressedTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
width, height, depth, t->gl.internalFormat, imageSize, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth),
t->gl.internalFormat, imageSize, p.buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY);
glCompressedTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
width, height, depth, t->gl.internalFormat, imageSize, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth),
t->gl.internalFormat, imageSize, p.buffer);
break;
case SamplerType::SAMPLER_CUBEMAP: {
assert_invariant(faceOffsets);
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
FaceOffsets const& offsets = *faceOffsets;
assert_invariant(width == height);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
p.format, p.type, p.stride ? p.stride : width, height, p.alignment);
UTILS_NOUNROLL
for (size_t face = 0; face < 6; face++) {
GLenum target = getCubemapTarget(face);
glCompressedTexSubImage2D(target, GLint(level), 0, 0,
width, height, t->gl.internalFormat,
imageSize, static_cast<uint8_t const*>(p.buffer) + offsets[face]);
for (size_t face = 0; face < depth; face++) {
GLenum target = getCubemapTarget(zoffset + face);
glCompressedTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), t->gl.internalFormat,
imageSize, static_cast<uint8_t const*>(p.buffer) + faceSize * face);
}
break;
}

View File

@@ -283,13 +283,13 @@ private:
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& data, FaceOffsets const* faceOffsets);
PixelBufferDescriptor&& p);
void setCompressedTextureData(GLTexture* t,
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& data, FaceOffsets const* faceOffsets);
PixelBufferDescriptor&& p);
void renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width,
uint32_t height, uint8_t samples) const noexcept;

View File

@@ -91,7 +91,11 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
return nullptr;
}
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
// PlatofrmEGL is used with and without GLES, but this function is only
// meaningful when GLES is used.
importGLESExtensionsEntryPoints();
#endif
auto extensions = GLUtils::split(eglQueryString(mEGLDisplay, EGL_EXTENSIONS));

View File

@@ -33,11 +33,11 @@ namespace filament {
using namespace backend;
namespace glext {
extern PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
extern PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
extern PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
extern PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
extern PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
UTILS_PRIVATE PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = {};
UTILS_PRIVATE PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = {};
UTILS_PRIVATE PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = {};
UTILS_PRIVATE PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = {};
UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
}
using namespace glext;
@@ -47,7 +47,7 @@ PlatformEGLHeadless::PlatformEGLHeadless() noexcept
: PlatformEGL() {
}
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext) noexcept {
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
if (UTILS_UNLIKELY(!bindAPI)) {
slog.e << "eglBindAPI EGL_OPENGL_API failed" << io::endl;
@@ -138,6 +138,17 @@ backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext) noexcept
goto error;
}
// fallback to a 24-bit depth buffer
if (configsCount == 0) {
configAttribs[10] = EGL_DEPTH_SIZE;
configAttribs[11] = 24;
if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount)) {
logEglError("eglChooseConfig");
goto error;
}
}
// find a transparent config
configAttribs[8] = EGL_ALPHA_SIZE;
configAttribs[9] = 8;
@@ -188,7 +199,7 @@ backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext) noexcept
clearGlError();
// success!!
return OpenGLDriverFactory::create(this, sharedContext);
return OpenGLDriverFactory::create(this, sharedContext, driverConfig);
error:
// if we're here, we've failed

View File

@@ -19,29 +19,18 @@
#include "PlatformEGL.h"
namespace filament {
namespace filament::backend {
class PlatformEGLHeadless final : public PlatformEGL {
public:
PlatformEGLHeadless() noexcept;
backend::Driver* createDriver(void* sharedContext) noexcept final;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept final;
int getOSVersion() const noexcept final { return 0; }
void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept final {}
Stream* createStream(void* nativeStream) noexcept final { return nullptr; }
void destroyStream(Stream* stream) noexcept final {}
void attach(Stream* stream, intptr_t tname) noexcept final {}
void detach(Stream* stream) noexcept final {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final {}
ExternalTexture* createExternalTextureStorage() noexcept final { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, backend::TextureFormat format) noexcept final {}
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final {}
void createExternalImageTexture(void* texture) noexcept final {}
void destroyExternalImage(void* texture) noexcept final {}
};
} // namespace filament

View File

@@ -401,16 +401,6 @@ io::ostream& operator<<(io::ostream& out, const AttributeArray& type) {
return out << "AttributeArray[" << type.max_size() << "]{}";
}
io::ostream& operator<<(io::ostream& out, const FaceOffsets& type) {
return out << "FaceOffsets{"
<< type[0] << ", "
<< type[1] << ", "
<< type[2] << ", "
<< type[3] << ", "
<< type[4] << ", "
<< type[5] << "}";
}
io::ostream& operator<<(io::ostream& out, const RasterState& rs) {
// TODO: implement decoding of enums
return out << "RasterState{"

View File

@@ -937,14 +937,6 @@ void VulkanDriver::resetBufferObject(Handle<HwBufferObject> boh) {
// This is only useful if updateBufferObjectUnsynchronized() is implemented unsynchronizedly.
}
void VulkanDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
handle_cast<VulkanTexture*>(th)->updateImage(data, width, height, 1,
xoffset, yoffset, 0, level);
scheduleDestroy(std::move(data));
}
void VulkanDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
handle_cast<VulkanTexture*>(th)->setPrimaryRange(minLevel, maxLevel);
}
@@ -959,12 +951,6 @@ void VulkanDriver::update3DImage(
scheduleDestroy(std::move(data));
}
void VulkanDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
handle_cast<VulkanTexture*>(th)->updateCubeImage(data, faceOffsets, level);
scheduleDestroy(std::move(data));
}
void VulkanDriver::setupExternalImage(void* image) {
}
@@ -1957,8 +1943,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
"loadUniformBuffer",
"updateBufferObject",
"updateIndexBuffer",
"update2DImage",
"updateCubeImage",
"update3DImage",
};
static const utils::StaticString BEGIN_COMMAND = "beginRenderPass";
static const utils::StaticString END_COMMAND = "endRenderPass";

View File

@@ -205,7 +205,9 @@ VulkanTexture::~VulkanTexture() {
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
assert_invariant(width <= this->width && height <= this->height && depth <= this->depth);
assert_invariant(width <= this->width && height <= this->height);
assert_invariant(depth <= this->depth * (target == SamplerType::SAMPLER_CUBEMAP ? 6 : 1));
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
@@ -260,7 +262,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
};
// Vulkan specifies subregions for 3D textures differently than from 2D arrays.
if (target == SamplerType::SAMPLER_2D_ARRAY) {
if (target == SamplerType::SAMPLER_2D_ARRAY || target == SamplerType::SAMPLER_CUBEMAP) {
copyRegion.imageOffset.z = 0;
copyRegion.imageExtent.depth = 1;
copyRegion.imageSubresource.baseArrayLayer = zoffset;
@@ -313,52 +315,6 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
transitionLayout(cmdbuffer, range, getDefaultImageLayout(usage));
}
void VulkanTexture::updateCubeImage(const PixelBufferDescriptor& data,
const FaceOffsets& faceOffsets, uint32_t miplevel) {
assert_invariant(this->target == SamplerType::SAMPLER_CUBEMAP);
const bool reshape = getBytesPerPixel(format) == 3;
const void* cpuData = data.buffer;
const uint32_t numSrcBytes = data.size;
const uint32_t numDstBytes = reshape ? (4 * numSrcBytes / 3) : numSrcBytes;
// Create and populate the staging buffer.
VulkanStage const* stage = mStagePool.acquireStage(numDstBytes);
void* mapped;
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
if (reshape) {
DataReshaper::reshape<uint8_t, 3, 4>(mapped, cpuData, numSrcBytes);
} else {
memcpy(mapped, cpuData, numSrcBytes);
}
vmaUnmapMemory(mContext.allocator, stage->memory);
vmaFlushAllocation(mContext.allocator, stage->memory, 0, numDstBytes);
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
const uint32_t width = std::max(1u, this->width >> miplevel);
const uint32_t height = std::max(1u, this->height >> miplevel);
const VkImageSubresourceRange range = { getImageAspect(), miplevel, 1, 0, 6 };
const VkImageLayout textureLayout = getDefaultImageLayout(usage);
transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy regions[6] = {{}};
VkExtent3D extent { width, height, 1 };
for (size_t face = 0; face < 6; face++) {
auto& region = regions[face];
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.baseArrayLayer = face;
region.imageSubresource.layerCount = 1;
region.imageSubresource.mipLevel = miplevel;
region.imageExtent = extent;
region.bufferOffset = faceOffsets.offsets[face];
}
vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6, regions);
transitionLayout(cmdbuffer, range, textureLayout);
}
void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel) {
maxMiplevel = filament::math::min(int(maxMiplevel), int(this->levels - 1));
mPrimaryViewRange.baseMipLevel = minMiplevel;

View File

@@ -43,10 +43,6 @@ struct VulkanTexture : public HwTexture {
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel);
// Uploads data into all 6 faces of a cubemap for a given miplevel.
void updateCubeImage(const PixelBufferDescriptor& data, const FaceOffsets& faceOffsets,
uint32_t miplevel);
// Returns the primary image view, which is used for shader sampling.
VkImageView getPrimaryImageView() const { return mCachedImageViews.at(mPrimaryViewRange); }

View File

@@ -109,12 +109,10 @@ void BackendTest::fullViewport(Viewport& viewport) {
viewport.height = view.height;
}
void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
Handle<HwSwapChain> swapChain, Handle<HwProgram> program) {
auto& api = getDriverApi();
TrianglePrimitive triangle(api);
void BackendTest::renderTriangle(
filament::backend::Handle<filament::backend::HwRenderTarget> renderTarget,
filament::backend::Handle<filament::backend::HwSwapChain> swapChain,
filament::backend::Handle<filament::backend::HwProgram> program) {
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
@@ -123,6 +121,14 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
renderTriangle(renderTarget, swapChain, program, params);
}
void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
Handle<HwSwapChain> swapChain, Handle<HwProgram> program, const RenderPassParams& params) {
auto& api = getDriverApi();
TrianglePrimitive triangle(api);
api.makeCurrent(swapChain, swapChain);

View File

@@ -54,6 +54,10 @@ protected:
void renderTriangle(filament::backend::Handle<filament::backend::HwRenderTarget> renderTarget,
filament::backend::Handle<filament::backend::HwSwapChain> swapChain,
filament::backend::Handle<filament::backend::HwProgram> program);
void renderTriangle(filament::backend::Handle<filament::backend::HwRenderTarget> renderTarget,
filament::backend::Handle<filament::backend::HwSwapChain> swapChain,
filament::backend::Handle<filament::backend::HwProgram> program,
const filament::backend::RenderPassParams& params);
void readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
filament::backend::Handle<filament::backend::HwRenderTarget> rt, uint32_t expectedHash,

View File

@@ -27,7 +27,7 @@ static constexpr filament::math::float2 gVertices[3] = {
{ -1.0, 1.0 }
};
static constexpr short gIndices[3] = { 0, 1, 2 };
static constexpr TrianglePrimitive::index_type gIndices[3] = { 0, 1, 2 };
TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
bool allocateLargeBuffers) : mDriverApi(driverApi) {
@@ -53,9 +53,10 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
BufferDescriptor vertexBufferDesc(gVertices, size);
mDriverApi.updateBufferObject(mBufferObject, std::move(vertexBufferDesc), 0);
mIndexBuffer = mDriverApi.createIndexBuffer(ElementType::SHORT, mIndexCount,
BufferUsage::STATIC);
BufferDescriptor indexBufferDesc(gIndices, sizeof(short) * 3);
ElementType elementType = ElementType::UINT;
static_assert(sizeof(index_type) == 4);
mIndexBuffer = mDriverApi.createIndexBuffer(elementType, mIndexCount, BufferUsage::STATIC);
BufferDescriptor indexBufferDesc(gIndices, sizeof(index_type) * 3);
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(indexBufferDesc), 0);
mRenderPrimitive = mDriverApi.createRenderPrimitive(
@@ -74,28 +75,28 @@ void TrianglePrimitive::updateVertices(const filament::math::float2 vertices[3])
mDriverApi.updateBufferObject(mBufferObject, std::move(vBuffer), 0);
}
void TrianglePrimitive::updateIndices(const short indices[3]) noexcept {
void* buffer = malloc(sizeof(short) * mIndexCount);
short* indexBuffer = (short*) buffer;
void TrianglePrimitive::updateIndices(const index_type indices[3]) noexcept {
void* buffer = malloc(sizeof(index_type) * mIndexCount);
index_type* indexBuffer = (index_type*) buffer;
std::copy(indices, indices + 3, indexBuffer);
BufferDescriptor bufferDesc(indexBuffer, sizeof(short) * mIndexCount,
BufferDescriptor bufferDesc(indexBuffer, sizeof(index_type) * mIndexCount,
[] (void* buffer, size_t size, void* user) {
free(buffer);
});
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(bufferDesc), 0);
}
void TrianglePrimitive::updateIndices(const short* indices, int count, int offset) noexcept {
void* buffer = malloc(sizeof(short) * count);
short* indexBuffer = (short*) buffer;
void TrianglePrimitive::updateIndices(const index_type* indices, int count, int offset) noexcept {
void* buffer = malloc(sizeof(index_type) * count);
index_type* indexBuffer = (index_type*) buffer;
std::copy(indices, indices + count, indexBuffer);
BufferDescriptor bufferDesc(indexBuffer, sizeof(short) * count,
BufferDescriptor bufferDesc(indexBuffer, sizeof(index_type) * count,
[] (void* buffer, size_t size, void* user) {
free(buffer);
});
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(bufferDesc), offset * sizeof(short));
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(bufferDesc), offset * sizeof(index_type));
}
TrianglePrimitive::~TrianglePrimitive() {

View File

@@ -43,14 +43,16 @@ public:
using VertexHandle = filament::backend::Handle<filament::backend::HwVertexBuffer>;
using IndexHandle = filament::backend::Handle<filament::backend::HwIndexBuffer>;
using index_type = uint32_t;
TrianglePrimitive(filament::backend::DriverApi& driverApi, bool allocateLargeBuffers = false);
~TrianglePrimitive();
PrimitiveHandle getRenderPrimitive() const noexcept;
void updateVertices(const filament::math::float2 vertices[3]) noexcept;
void updateIndices(const short* indices) noexcept;
void updateIndices(const short* indices, int count, int offset) noexcept;
void updateIndices(const index_type* indices) noexcept;
void updateIndices(const index_type* indices, int count, int offset) noexcept;
private:

View File

@@ -176,7 +176,6 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
// Draw a circle on a yellow background.
uint32_t* texels = (uint32_t*) buffer0;
FaceOffsets offsets;
for (int face = 0; face < 6; face++) {
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
@@ -187,12 +186,11 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
texels[row * width + col] = toUintColor(float4(color, 1.0f));
}
}
offsets[face] = face * width * height * 4;
texels += offsets[face] / 4;
texels += face * width * height;
}
// Upload to the GPU.
dapi.updateCubeImage(texture, level, std::move(pb), offsets);
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
}
TEST_F(BackendTest, CubemapMinify) {

View File

@@ -31,8 +31,15 @@ layout(location = 0) in vec4 mesh_position;
layout(location = 0) out uvec4 indices;
uniform Params {
highp vec4 padding[4]; // offset of 64 bytes
highp vec4 color;
highp vec4 offset;
} params;
void main() {
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
gl_Position = vec4(mesh_position.xy + params.offset.xy, 0.0, 1.0);
#if defined(TARGET_VULKAN_ENVIRONMENT)
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
gl_Position.y = -gl_Position.y;
@@ -47,13 +54,12 @@ layout(location = 0) out vec4 fragColor;
uniform Params {
highp vec4 padding[4]; // offset of 64 bytes
highp float red;
highp float green;
highp float blue;
highp vec4 color;
highp vec4 offset;
} params;
void main() {
fragColor = vec4(params.red, params.green, params.blue, 1.0f);
fragColor = vec4(params.color.rgb, 1.0f);
}
)");
@@ -65,6 +71,14 @@ namespace test {
using namespace filament;
using namespace filament::backend;
// In the shader, these MaterialParams are offset by 64 bytes into the uniform buffer to test buffer
// updates with offset.
struct MaterialParams {
math::float4 color;
math::float4 offset;
};
static_assert(sizeof(MaterialParams) == 8 * sizeof(float));
TEST_F(BackendTest, VertexBufferUpdate) {
const bool largeBuffers = false;
@@ -104,13 +118,34 @@ TEST_F(BackendTest, VertexBufferUpdate) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
// Create a uniform buffer.
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
// GPU buffer, which is more interesting to test.
auto ubuffer = getDriverApi().createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
getDriverApi().bindUniformBuffer(0, ubuffer);
getDriverApi().startCapture(0);
// Upload uniforms.
{
MaterialParams params {
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64);
}
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0);
// Draw 10 triangles, updating the vertex buffer / index buffer each time.
getDriverApi().beginRenderPass(defaultRenderTarget, params);
size_t triangleIndex = 0;
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
const float low = i, high = i + 0.2;
@@ -120,21 +155,28 @@ TEST_F(BackendTest, VertexBufferUpdate) {
if (updateIndices) {
if (triangleIndex % 2 == 0) {
// Upload each index separately, to test offsets.
const short i[3] {0, 1, 2};
const TrianglePrimitive::index_type i[3] {0, 1, 2};
triangle.updateIndices(i + 0, 1, 0);
triangle.updateIndices(i + 1, 1, 1);
triangle.updateIndices(i + 2, 1, 2);
} else {
// This effectively hides this triangle.
const short i[3] {0, 0, 0};
const TrianglePrimitive::index_type i[3] {0, 0, 0};
triangle.updateIndices(i);
}
}
if (triangleIndex > 0) {
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
}
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
triangleIndex++;
}
getDriverApi().endRenderPass();
getDriverApi().flush();
getDriverApi().commit(swapChain);
@@ -150,12 +192,8 @@ TEST_F(BackendTest, VertexBufferUpdate) {
executeCommands();
}
struct MaterialParams {
float red;
float green;
float blue;
};
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
// Create a platform-specific SwapChain and make it current.
auto swapChain = createSwapChain();
@@ -168,32 +206,65 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
auto program = getDriverApi().createProgram(std::move(p));
// Create a uniform buffer.
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
// GPU buffer, which is more interesting to test.
auto ubuffer = getDriverApi().createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
getDriverApi().bindUniformBuffer(0, ubuffer);
// Upload uniforms.
// Create a render target.
auto colorTexture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0, 512, 512, 1, {{colorTexture}}, {}, {});
// Upload uniforms for the first triangle.
{
MaterialParams params {
.red = 1.0f,
.green = 0.0f,
.blue = 0.5f
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
};
MaterialParams* tmp = new MaterialParams(params);
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
MaterialParams* sp = (MaterialParams*) buffer;
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64);
}
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 0.f, 1.f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
renderTriangle(renderTarget, swapChain, program, params);
renderTriangle(defaultRenderTarget, swapChain, program);
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
// color.b, color.a, offset.x, and offset.y.
{
MaterialParams params {
.color = { 1.0f, 0.0f, 1.0f, 1.0f },
.offset = { 0.5f, 0.5f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) ((char*)buffer - offsetof(MaterialParams, color.b));
delete sp;
};
BufferDescriptor bd((char*)tmp + offsetof(MaterialParams, color.b), sizeof(float) * 4, cb);
getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b));
}
static const uint32_t expectedHash = 2000773999;
readPixelsAndAssertHash("BufferObjectUpdateWithOffset", 512, 512, defaultRenderTarget, expectedHash);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
renderTriangle(renderTarget, swapChain, program, params);
static const uint32_t expectedHash = 91322442;
readPixelsAndAssertHash(
"BufferObjectUpdateWithOffset", 512, 512, renderTarget, expectedHash, true);
getDriverApi().flush();
getDriverApi().commit(swapChain);
@@ -201,7 +272,9 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
getDriverApi().destroyBufferObject(ubuffer);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(colorTexture);
// This ensures all driver commands have finished before exiting the test.
getDriverApi().finish();

View File

@@ -168,7 +168,7 @@ TEST_F(BackendTest, FeedbackLoops) {
}
auto cb = [](void* buffer, size_t size, void* user) { free(buffer); };
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
api.update2DImage(texture, 0, 0, 0, kTexWidth, kTexHeight, std::move(pb));
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
for (int frame = 0; frame < kNumFrames; frame++) {

View File

@@ -309,7 +309,7 @@ TEST_F(BackendTest, UpdateImage2D) {
testCases.emplace_back("RGB, UINT_10F_11F_11F_REV -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
testCases.emplace_back("RGB, HALF -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
/* // Test integer format uploads. */
// Test integer format uploads.
// TODO: These cases fail on OpenGL and Vulkan.
testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI);
testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI);
@@ -363,14 +363,14 @@ TEST_F(BackendTest, UpdateImage2D) {
PixelBufferDescriptor subregion2 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
PixelBufferDescriptor subregion3 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
PixelBufferDescriptor subregion4 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
api.update2DImage(texture, 0, 0, 0, 256, 256, std::move(subregion1));
api.update2DImage(texture, 0, 256, 0, 256, 256, std::move(subregion2));
api.update2DImage(texture, 0, 0, 256, 256, 256, std::move(subregion3));
api.update2DImage(texture, 0, 256, 256, 256, 256, std::move(subregion4));
api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1, std::move(subregion1));
api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1, std::move(subregion2));
api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1, std::move(subregion3));
api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1, std::move(subregion4));
} else {
PixelBufferDescriptor descriptor
= checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding);
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
}
SamplerGroup samplers(1);
@@ -454,7 +454,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
}
}
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.beginFrame(0, 0);
@@ -524,7 +524,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
// Create image data.
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
api.update2DImage(texture, 1, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.beginFrame(0, 0);

View File

@@ -68,6 +68,9 @@ class UTILS_PUBLIC Texture : public FilamentAPI {
public:
static constexpr const size_t BASE_LEVEL = 0;
//! Face offsets for all faces of a cubemap
struct FaceOffsets;
using PixelBufferDescriptor = backend::PixelBufferDescriptor; //!< Geometry of a pixel buffer
using Sampler = backend::SamplerType; //!< Type of sampler
using InternalFormat = backend::TextureFormat; //!< Internal texel format
@@ -75,7 +78,6 @@ public:
using Format = backend::PixelDataFormat; //!< Pixel color format
using Type = backend::PixelDataType; //!< Pixel data format
using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format
using FaceOffsets = backend::FaceOffsets; //!< Cube map faces offsets
using Usage = backend::TextureUsage; //!< Usage affects texel layout
using Swizzle = backend::TextureSwizzle; //!< Texture swizzle
@@ -133,7 +135,8 @@ public:
* effectively create a 3D texture.
* @param depth Depth of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_ARRAY or it has no effect.
* @attention This Texture instance must use Sampler::SAMPLER_3D or
* Sampler::SAMPLER_2D_ARRAY or it has no effect.
*/
Builder& depth(uint32_t depth) noexcept;
@@ -289,59 +292,8 @@ public:
InternalFormat getFormat() const noexcept;
/**
* Specify the image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with driver::SamplerType::SAMPLER_2D.
*
* @note
* This is equivalent to calling:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param xoffset Left offset of the sub-region to update.
* @param yoffset Bottom offset of the sub-region to update.
* @param width Width of the sub-region to update.
* @param height Height of the sub-region to update.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with Sampler::SAMPLER_2D.
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 3D texture or 2D texture array for a level.
* Updates a sub-image of a 3D texture or 2D texture array for a level. Cubemaps are treated
* like a 2D array of six layers.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
@@ -356,7 +308,8 @@ public:
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_array.
* @attention This Texture instance must use Sampler::SAMPLER_3D, Sampler::SAMPLER_2D_ARRAY
* or Sampler::SAMPLER_CUBEMAP.
*
* @see Builder::sampler()
*/
@@ -365,6 +318,32 @@ public:
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& buffer) const;
/**
* inline helper to update a 2D texture
*
* @see setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
inline void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const {
setImage(engine, level, 0, 0, 0, getWidth(level), getHeight(level), 1, std::move(buffer));
}
/**
* inline helper to update a 2D texture
*
* @see setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
inline void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const {
setImage(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer));
}
/**
* Specify all six images of a cube map level.
*
@@ -382,7 +361,13 @@ public:
* @attention This Texture instance must use Sampler::SAMPLER_CUBEMAP or it has no effect
*
* @see Texture::CubemapFace, Builder::sampler()
*
* @deprecated Instead, use setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
[[deprecated]]
void setImage(Engine& engine, size_t level,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
@@ -510,6 +495,51 @@ public:
void generatePrefilterMipmap(Engine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* options = nullptr);
/** @deprecated */
struct FaceOffsets {
using size_type = size_t;
union {
struct {
size_type px; //!< +x face offset in bytes
size_type nx; //!< -x face offset in bytes
size_type py; //!< +y face offset in bytes
size_type ny; //!< -y face offset in bytes
size_type pz; //!< +z face offset in bytes
size_type nz; //!< -z face offset in bytes
};
size_type offsets[6];
};
size_type operator[](size_t n) const noexcept { return offsets[n]; }
size_type& operator[](size_t n) { return offsets[n]; }
FaceOffsets() noexcept = default;
explicit FaceOffsets(size_type faceSize) noexcept {
px = faceSize * 0;
nx = faceSize * 1;
py = faceSize * 2;
ny = faceSize * 3;
pz = faceSize * 4;
nz = faceSize * 5;
}
FaceOffsets(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
}
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
return *this;
}
};
};
} // namespace filament

View File

@@ -502,8 +502,8 @@ std::pair<size_t, size_t> Froxelizer::clipToIndices(float2 const& clip) const no
void Froxelizer::commit(backend::DriverApi& driverApi) {
// send data to GPU
driverApi.update2DImage(mFroxelTexture, 0, 0, 0,
FROXEL_BUFFER_WIDTH, FROXEL_BUFFER_HEIGHT,{
driverApi.update3DImage(mFroxelTexture, 0, 0, 0, 0,
FROXEL_BUFFER_WIDTH, FROXEL_BUFFER_HEIGHT, 1, {
mFroxelBufferUser.begin(), mFroxelBufferUser.sizeInBytes(),
PixelBufferDescriptor::PixelDataFormat::RG_INTEGER,
PixelBufferDescriptor::PixelDataType::USHORT });

View File

@@ -266,33 +266,15 @@ void PostProcessManager::init() noexcept {
mStarburstTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::R8, 1, 256, 1, 1, TextureUsage::DEFAULT);
PixelBufferDescriptor dataOne(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataOneArray(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataZero(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataStarburst(driver.allocate(256), 256, PixelDataFormat::R, PixelDataType::UBYTE);
*static_cast<uint32_t *>(dataOne.buffer) = 0xFFFFFFFF;
*static_cast<uint32_t *>(dataOneArray.buffer) = 0xFFFFFFFF;
*static_cast<uint32_t *>(dataZero.buffer) = 0;
std::generate_n((uint8_t*)dataStarburst.buffer, 256,
[&dist = mUniformDistribution, &gen = mEngine.getRandomEngine()]() {
float r = 0.5f + 0.5f * dist(gen);
return uint8_t(r * 255.0f);
});
driver.update2DImage(engine.getOneTexture(),
0, 0, 0, 1, 1,
std::move(dataOne));
driver.update3DImage(engine.getOneTextureArray(),
0, 0, 0, 0, 1, 1, 1,
std::move(dataOneArray));
driver.update2DImage(engine.getZeroTexture(),
0, 0, 0, 1, 1,
std::move(dataZero));
driver.update2DImage(mStarburstTexture,
0, 0, 0, 256, 1,
driver.update3DImage(mStarburstTexture,
0, 0, 0, 0, 256, 1, 1,
std::move(dataStarburst));
}

View File

@@ -45,18 +45,6 @@ Texture::InternalFormat Texture::getFormat() const noexcept {
return upcast(this)->getFormat();
}
void Texture::setImage(Engine& engine, size_t level,
Texture::PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine), level, std::move(buffer));
}
void Texture::setImage(Engine& engine,
size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine),
level, xoffset, yoffset, width, height, std::move(buffer));
}
void Texture::setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,

View File

@@ -345,38 +345,29 @@ void FEngine::init() {
mDummyZeroTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
mDummyOneIntegerTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
TextureFormat::RGBA8I, 1, 1, 1, 1, TextureUsage::DEFAULT);
mDummyZeroTexture = driverApi.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
// initialize the dummy textures so that their contents are not undefined
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
static const uint32_t zeroes = 0;
static const uint32_t zeroes[6] = {0};
static const uint32_t ones = 0xffffffff;
static const uint32_t signedOnes = 0x7f7f7f7f;
mDefaultIblTexture->setImage(*this, 0,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE), {});
driverApi.update3DImage(mDefaultIblTexture->getHwHandle(), 0, 0, 0, 0, 1, 1, 6,
{ zeroes, sizeof(zeroes), Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update2DImage(mDummyOneTexture, 0, 0, 0, 1, 1,
PixelBufferDescriptor(&ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
driverApi.update3DImage(mDummyOneIntegerTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&signedOnes, 4, Texture::Format::RGBA_INTEGER, Texture::Type::BYTE));
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
mDefaultRenderTarget = driverApi.createDefaultRenderTarget();
@@ -467,7 +458,6 @@ void FEngine::shutdown() {
driver.destroyTexture(mDummyOneTexture);
driver.destroyTexture(mDummyOneTextureArray);
driver.destroyTexture(mDummyOneIntegerTextureArray);
driver.destroyTexture(mDummyZeroTexture);
driver.destroyTexture(mDummyZeroTextureArray);

View File

@@ -353,7 +353,6 @@ public:
backend::Handle<backend::HwTexture> getZeroTexture() const { return mDummyZeroTexture; }
backend::Handle<backend::HwTexture> getOneTextureArray() const { return mDummyOneTextureArray; }
backend::Handle<backend::HwTexture> getZeroTextureArray() const { return mDummyZeroTextureArray; }
backend::Handle<backend::HwTexture> getOneIntegerTextureArray() const { return mDummyOneIntegerTextureArray; }
static constexpr const size_t MiB = 1024u * 1024u;
size_t getMinCommandBufferSize() const noexcept { return mConfig.minCommandBufferSizeMB * MiB; }
@@ -470,7 +469,6 @@ private:
backend::Handle<backend::HwTexture> mDummyOneTexture;
backend::Handle<backend::HwTexture> mDummyOneTextureArray;
backend::Handle<backend::HwTexture> mDummyZeroTextureArray;
backend::Handle<backend::HwTexture> mDummyOneIntegerTextureArray;
backend::Handle<backend::HwTexture> mDummyZeroTexture;
std::thread::id mMainThreadId{};

View File

@@ -40,6 +40,16 @@ namespace filament {
using namespace backend;
using namespace math;
// this is a hack to be able to create a std::function<> with a non-copyable closure
template<class F>
auto make_copyable_function(F&& f) {
using dF = std::decay_t<F>;
auto spf = std::make_shared<dF>(std::forward<F>(f));
return [spf](auto&& ... args) -> decltype(auto) {
return (*spf)(decltype(args)(args)...);
};
}
struct Texture::BuilderDetails {
intptr_t mImportedId = 0;
uint32_t mWidth = 1;
@@ -139,11 +149,25 @@ Texture* Texture::Builder::build(Engine& engine) {
FTexture::FTexture(FEngine& engine, const Builder& builder) {
mWidth = static_cast<uint32_t>(builder->mWidth);
mHeight = static_cast<uint32_t>(builder->mHeight);
mDepth = static_cast<uint32_t>(builder->mDepth);
mFormat = builder->mFormat;
mUsage = builder->mUsage;
mTarget = builder->mTarget;
mDepth = static_cast<uint32_t>(builder->mDepth);
mLevelCount = std::min(builder->mLevels, FTexture::maxLevelCount(mWidth, mHeight));
uint8_t maxLevelCount;
switch (builder->mTarget) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_EXTERNAL:
maxLevelCount = FTexture::maxLevelCount(mWidth, mHeight);
break;
case SamplerType::SAMPLER_3D:
maxLevelCount = FTexture::maxLevelCount(std::max(mWidth, std::max(mHeight, mDepth)));
break;
}
mLevelCount = std::min(builder->mLevels, maxLevelCount);
FEngine::DriverApi& driver = engine.getDriverApi();
if (UTILS_LIKELY(builder->mImportedId == 0)) {
@@ -180,76 +204,6 @@ size_t FTexture::getDepth(size_t level) const noexcept {
return valueForLevel(level, mDepth);
}
void FTexture::setImage(FEngine& engine, size_t level,
Texture::PixelBufferDescriptor&& buffer) const {
setImage(engine, level, 0, 0,
uint32_t(getWidth(level)), uint32_t(getHeight(level)),
std::move(buffer));
}
UTILS_NOINLINE
void FTexture::setImage(FEngine& engine,
size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
Texture::PixelBufferDescriptor&& buffer) const {
auto validateTarget = [](SamplerType sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_EXTERNAL:
return true;
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
return false;
}
};
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.type == PixelDataType::COMPRESSED ||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
"The combination of internal format=%u and {format=%u, type=%u} is not supported.",
unsigned(mFormat), unsigned(buffer.format), unsigned(buffer.type))) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(!mStream, "setImage() called on a Stream texture.")) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(level < mLevelCount,
"level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount))) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(validateTarget(mTarget),
"Texture Sampler type (%u) not supported for this operation.", unsigned(mTarget))) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.buffer, "Data buffer is nullptr.")) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(mSampleCount <= 1,
"Operation not supported with multisample (%u) texture.", unsigned(mSampleCount))) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(xoffset + width <= valueForLevel(level, mWidth),
"xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
unsigned(xoffset), unsigned(width), unsigned(valueForLevel(level, mWidth)), unsigned(level))) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(yoffset + height <= valueForLevel(level, mHeight),
"xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)), unsigned(level))) {
return;
}
engine.getDriverApi().update2DImage(mHandle,
uint8_t(level), xoffset, yoffset, width, height, std::move(buffer));
}
void FTexture::setImage(FEngine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
@@ -257,12 +211,12 @@ void FTexture::setImage(FEngine& engine, size_t level,
auto validateTarget = [](SamplerType sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
return true;
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_EXTERNAL:
case SamplerType::SAMPLER_CUBEMAP:
return true;
case SamplerType::SAMPLER_EXTERNAL:
return false;
}
};
@@ -305,11 +259,28 @@ void FTexture::setImage(FEngine& engine, size_t level,
return;
}
// effective level is just how we compute the index/depth based on whether we're an array or a 3D texture
const uint8_t effectiveLevel = mTarget == SamplerType::SAMPLER_3D ? level : 0;
if (!ASSERT_POSTCONDITION_NON_FATAL(zoffset + depth <= valueForLevel(effectiveLevel, mDepth),
uint32_t textureDepthOrLayers;
switch (mTarget) {
case SamplerType::SAMPLER_EXTERNAL:
// can't happen by construction, fallthrough...
case SamplerType::SAMPLER_2D:
assert_invariant(mDepth == 1);
textureDepthOrLayers = mDepth;
break;
case SamplerType::SAMPLER_3D:
textureDepthOrLayers = valueForLevel(level, mDepth);
break;
case SamplerType::SAMPLER_2D_ARRAY:
textureDepthOrLayers = mDepth;
break;
case SamplerType::SAMPLER_CUBEMAP:
textureDepthOrLayers = 6;
break;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(zoffset + depth <= textureDepthOrLayers,
"zoffset (%u) + depth (%u) > texture depth (%u) at level (%u)",
unsigned(zoffset), unsigned(depth), unsigned(valueForLevel(effectiveLevel, mDepth)), unsigned(level))) {
unsigned(zoffset), unsigned(depth), textureDepthOrLayers, unsigned(level))) {
return;
}
@@ -321,6 +292,7 @@ void FTexture::setImage(FEngine& engine, size_t level,
uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(buffer));
}
// deprecated
void FTexture::setImage(FEngine& engine, size_t level,
Texture::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const {
@@ -328,9 +300,9 @@ void FTexture::setImage(FEngine& engine, size_t level,
switch (sampler) {
case SamplerType::SAMPLER_CUBEMAP:
return true;
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_EXTERNAL:
return false;
}
@@ -361,8 +333,19 @@ void FTexture::setImage(FEngine& engine, size_t level,
return;
}
engine.getDriverApi().updateCubeImage(mHandle, uint8_t(level),
std::move(buffer), faceOffsets);
auto w = std::max(1u, mWidth >> level);
auto h = std::max(1u, mHeight >> level);
assert_invariant(w == h);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(buffer.format, buffer.type,
buffer.stride ? buffer.stride : w, h, buffer.alignment);
UTILS_NOUNROLL
for (size_t face = 0; face < 6; face++) {
engine.getDriverApi().update3DImage(mHandle, uint8_t(level), 0, 0, face, w, h, 1, {
(char*)buffer.buffer + faceOffsets[face],
faceSize, buffer.format, buffer.type, buffer.alignment,
buffer.left, buffer.top, buffer.stride });
}
engine.getDriverApi().queueCommand(make_copyable_function([buffer = std::move(buffer)]() {}));
}
void FTexture::setExternalImage(FEngine& engine, void* image) noexcept {
@@ -495,16 +478,6 @@ size_t FTexture::getFormatSize(InternalFormat format) noexcept {
}
// this is a hack to be able to create a std::function<> with a non-copyable closure
template<class F>
auto make_copyable_function(F&& f) {
using dF = std::decay_t<F>;
auto spf = std::make_shared<dF>(std::forward<F>(f));
return [spf](auto&& ... args) -> decltype(auto) {
return (*spf)(decltype(args)(args)...);
};
}
void FTexture::generatePrefilterMipmap(FEngine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* options) {
@@ -676,13 +649,16 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
Texture::PixelBufferDescriptor::PixelDataType::FLOAT, 1, 0, 0, image.getStride());
uintptr_t base = uintptr_t(image.getData());
backend::FaceOffsets offsets{};
for (size_t j = 0; j < 6; j++) {
Image const& faceImage = dst.getImageForFace((Cubemap::Face)j);
offsets[j] = uintptr_t(faceImage.getData()) - base;
auto offset = uintptr_t(faceImage.getData()) - base;
driver.update3DImage(mHandle, level, 0, 0, j, dim, dim, 1, {
(char*)image.getData() + offset, dim * dim * 3 * sizeof(float),
Texture::PixelBufferDescriptor::PixelDataFormat::RGB,
Texture::PixelBufferDescriptor::PixelDataType::FLOAT, 1,
0, 0, uint32_t(image.getStride())
});
}
// upload all 6 faces into the texture
driver.updateCubeImage(mHandle, level, std::move(pbd), offsets);
// enqueue a commands that holds the image data until it's executed
driver.queueCommand(make_copyable_function([data = image.detach()]() {}));

View File

@@ -43,22 +43,16 @@ public:
size_t getHeight(size_t level = 0) const noexcept;
size_t getDepth(size_t level = 0) const noexcept;
size_t getLevelCount() const noexcept { return mLevelCount; }
size_t getMaxLevelCount() const noexcept { return FTexture::maxLevelCount(mWidth, mHeight); }
Sampler getTarget() const noexcept { return mTarget; }
InternalFormat getFormat() const noexcept { return mFormat; }
Usage getUsage() const noexcept { return mUsage; }
void setImage(FEngine& engine, size_t level, PixelBufferDescriptor&& buffer) const;
void setImage(FEngine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const;
void setImage(FEngine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& buffer) const;
[[deprecated]]
void setImage(FEngine& engine, size_t level,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
@@ -105,12 +99,13 @@ public:
// Returns the max number of levels for a texture of given max dimensions
static inline uint8_t maxLevelCount(uint32_t maxDimension) noexcept {
return std::max(1, std::ilogbf(maxDimension) + 1);
return std::max(1, std::ilogbf(float(maxDimension)) + 1);
}
// Returns the max number of levels for a texture of given dimensions
static inline uint8_t maxLevelCount(uint32_t width, uint32_t height) noexcept {
return std::max(1, std::ilogbf(std::max(width, height)) + 1);
uint32_t maxDimension = std::max(width, height);
return maxLevelCount(maxDimension);
}
static bool validatePixelFormatAndType(backend::TextureFormat internalFormat,

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.25.3"
spec.version = "1.25.4"
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.25.3/filament-v1.25.3-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.25.4/filament-v1.25.4-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -20,7 +20,7 @@
namespace bluegl {
struct Driver {
void* (*eglGetProcAddress)(const GLubyte*);
void* (*eglGetProcAddress)(const char*);
void* library;
} g_driver = {nullptr, nullptr};
@@ -32,14 +32,14 @@ bool initBinder() {
return false;
}
g_driver.eglGetProcAddress = (void *(*)(const GLubyte *))
g_driver.eglGetProcAddress = (void *(*)(const char *))
dlsym(g_driver.library, "eglGetProcAddress");
return g_driver.eglGetProcAddress;
}
void* loadFunction(const char* name) {
return (void*) g_driver.eglGetProcAddress((const GLubyte*) name);
return (void*) g_driver.eglGetProcAddress((const char*) name);
}
void shutdownBinder() {

View File

@@ -64,7 +64,7 @@ private:
bool loadCubemapLevel(filament::Texture** texture,
filament::Texture::PixelBufferDescriptor* outBuffer,
filament::Texture::FaceOffsets* outOffsets,
uint32_t* dim,
const utils::Path& path,
size_t level = 0, std::string const& levelPrefix = "") const;

View File

@@ -196,10 +196,10 @@ bool IBL::loadFromDirectory(const utils::Path& path) {
bool IBL::loadCubemapLevel(filament::Texture** texture, const utils::Path& path, size_t level,
std::string const& levelPrefix) const {
Texture::FaceOffsets offsets;
uint32_t dim;
Texture::PixelBufferDescriptor buffer;
if (loadCubemapLevel(texture, &buffer, &offsets, path, level, levelPrefix)) {
(*texture)->setImage(mEngine, level, std::move(buffer), offsets);
if (loadCubemapLevel(texture, &buffer, &dim, path, level, levelPrefix)) {
(*texture)->setImage(mEngine, level, 0, 0, 0, dim, dim, 6, std::move(buffer));
return true;
}
return false;
@@ -208,7 +208,7 @@ bool IBL::loadCubemapLevel(filament::Texture** texture, const utils::Path& path,
bool IBL::loadCubemapLevel(
filament::Texture** texture,
Texture::PixelBufferDescriptor* outBuffer,
Texture::FaceOffsets* outOffsets,
uint32_t* dim,
const utils::Path& path, size_t level, std::string const& levelPrefix) const {
static const char* faceSuffix[6] = { "px", "nx", "py", "ny", "pz", "nz" };
@@ -248,8 +248,8 @@ bool IBL::loadCubemapLevel(
// RGB_10_11_11_REV encoding: 4 bytes per pixel
const size_t faceSize = size * size * sizeof(uint32_t);
*dim = size;
Texture::FaceOffsets offsets;
Texture::PixelBufferDescriptor buffer(
malloc(faceSize * 6), faceSize * 6,
Texture::Format::RGB, Texture::Type::UINT_10F_11F_11F_REV,
@@ -259,8 +259,6 @@ bool IBL::loadCubemapLevel(
uint8_t* p = static_cast<uint8_t*>(buffer.buffer);
for (size_t j = 0; j < 6; j++) {
offsets[j] = faceSize * j;
std::string faceName = levelPrefix + faceSuffix[j] + ".rgb32f";
Path facePath(Path::concat(path, faceName));
if (!facePath.exists()) {
@@ -284,7 +282,7 @@ bool IBL::loadCubemapLevel(
break;
}
memcpy(p + offsets[j], data, w * h * sizeof(uint32_t));
memcpy(p + faceSize * j, data, w * h * sizeof(uint32_t));
stbi_image_free(data);
}
@@ -292,7 +290,6 @@ bool IBL::loadCubemapLevel(
if (!success) return false;
*outBuffer = std::move(buffer);
*outOffsets = offsets;
return true;
}

View File

@@ -76,25 +76,30 @@ Texture* createTexture(Engine* engine, const Ktx1Bundle& ktx, bool srgb,
if (isCompressed(ktxinfo)) {
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
ktx.getBlob({ level, 0, 0 }, &data, &size);
const uint32_t dim = texture->getWidth(level);
texture->setImage(*engine, level, 0, 0, 0, dim, dim, 6, {
data, size * 6, cdatatype, size, cb, cbuser
});
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
ktx.getBlob({ level, 0, 0 }, &data, &size);
texture->setImage(*engine, level, {
data, size, cdatatype, size, cb, cbuser
});
}
return texture;
}
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
ktx.getBlob({ level, 0, 0 }, &data, &size);
const uint32_t dim = texture->getWidth(level);
texture->setImage(*engine, level, 0, 0, 0, dim, dim, 6, {
data, size * 6, dataformat, datatype, cb, cbuser
});
}
return texture;
}

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.25.3",
"version": "1.25.4",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",