Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb8914ab96 | ||
|
|
2fecda7bdc | ||
|
|
543d8efb25 | ||
|
|
396b1079a7 | ||
|
|
90f508e89d | ||
|
|
d70cee7fec | ||
|
|
9c542791ef | ||
|
|
2d1d6ffb2c | ||
|
|
9cc7fe97c5 | ||
|
|
61fb5a588e | ||
|
|
815d202f6d |
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
11
build.sh
11
build.sh
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
24.0.8215888
|
||||
25.0.8775105
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{"
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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()]() {}));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user