rework backend texture upload APIs

The main goal is to allow more flexibility, allow cubemap arrays in
the future and better match vk and metal apis.

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

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

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

A side effect of this change is that it is now possible to update only
a single face of a cubemap, but also a region of a face (or faces).
This commit is contained in:
Mathias Agopian
2022-07-21 11:12:05 -07:00
committed by Mathias Agopian
parent 815d202f6d
commit 61fb5a588e
29 changed files with 233 additions and 587 deletions
+2
View File
@@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created.
## main branch
- backend: streamline texture upload APIs [⚠️ **API Change**]
## v1.25.3
- engine: Fix Adreno gpu crash introduced by gpu morph target change
@@ -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,
@@ -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)
-18
View File
@@ -734,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) {
}
@@ -757,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
+3 -4
View File
@@ -198,12 +198,11 @@ struct MetalTexture : public HwTexture {
~MetalTexture();
void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept;
void loadCubeImage(const FaceOffsets& faceOffsets, int miplevel, PixelBufferDescriptor& p);
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor& data,
PixelBufferDescriptor const& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor& data,
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void updateLodRange(uint32_t level);
+4 -22
View File
@@ -641,6 +641,7 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
break;
}
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_2D_ARRAY: {
// Metal uses 'slice' (not z offset) to index into individual layers of a texture array.
const uint32_t slice = region.origin.z;
@@ -659,7 +660,6 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
break;
}
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_EXTERNAL: {
assert_invariant(false);
}
@@ -668,26 +668,8 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
updateLodRange(level);
}
void MetalTexture::loadCubeImage(const FaceOffsets& faceOffsets, int miplevel,
PixelBufferDescriptor& p) {
PixelBufferDescriptor* data = &p;
PixelBufferDescriptor reshapedData;
if(reshape(p, reshapedData)) {
data = &reshapedData;
}
const NSUInteger faceWidth = width >> miplevel;
for (NSUInteger slice = 0; slice < 6; slice++) {
FaceOffsets::size_type faceOffset = faceOffsets.offsets[slice];
loadSlice(miplevel, MTLRegionMake2D(0, 0, faceWidth, faceWidth), faceOffset, slice, *data);
}
updateLodRange((uint32_t) miplevel);
}
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor& data) noexcept {
PixelBufferDescriptor const& data) noexcept {
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
ASSERT_PRECONDITION(data.size >= shape.totalBytes,
@@ -722,7 +704,7 @@ void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffs
}
void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region,
PixelBufferDescriptor& data, const PixelBufferShape& shape) {
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.contents,
@@ -750,7 +732,7 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
}
void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region,
PixelBufferDescriptor& data, const PixelBufferShape& shape) {
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
MTLPixelFormat stagingPixelFormat = getMetalFormat(data.format, data.type);
MTLTextureDescriptor* descriptor = [MTLTextureDescriptor new];
descriptor.textureType = region.size.depth == 1 ? MTLTextureType2D : MTLTextureType3D;
-11
View File
@@ -207,12 +207,6 @@ void NoopDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t inde
Handle<HwBufferObject> boh) {
}
void NoopDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
scheduleDestroy(std::move(data));
}
void NoopDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
}
@@ -223,11 +217,6 @@ void NoopDriver::update3DImage(Handle<HwTexture> th,
scheduleDestroy(std::move(data));
}
void NoopDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
scheduleDestroy(std::move(data));
}
void NoopDriver::setupExternalImage(void* image) {
}
+41 -59
View File
@@ -1753,21 +1753,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
*sb->sb = std::move(samplerGroup); // NOLINT(performance-move-const-arg)
}
void OpenGLDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture *>(th);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t,
level, xoffset, yoffset, 0, width, height, 1, std::move(data), nullptr);
} else {
setTextureData(t,
level, xoffset, yoffset, 0, width, height, 1, std::move(data), nullptr);
}
}
void OpenGLDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -1795,24 +1780,10 @@ void OpenGLDriver::update3DImage(Handle<HwTexture> th,
GLTexture* t = handle_cast<GLTexture *>(th);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t,
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data), nullptr);
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data));
} else {
setTextureData(t,
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data), nullptr);
}
}
void OpenGLDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture *>(th);
auto width = std::max(1u, t->width >> level);
auto height = std::max(1u, t->height >> level);
if (data.type == PixelDataType::COMPRESSED) {
setCompressedTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
} else {
setTextureData(t, level, 0, 0, 0, width, height, 0, std::move(data), &faceOffsets);
level, xoffset, yoffset, zoffset, width, height, depth, std::move(data));
}
}
@@ -1842,11 +1813,10 @@ bool OpenGLDriver::canGenerateMipmaps() {
return true;
}
void OpenGLDriver::setTextureData(GLTexture* t,
uint32_t level,
void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p, FaceOffsets const* faceOffsets) {
PixelBufferDescriptor&& p) {
auto& gl = mContext;
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
@@ -1861,10 +1831,10 @@ void OpenGLDriver::setTextureData(GLTexture* t,
GLenum glFormat = getFormat(p.format);
GLenum glType = getType(p.type);
gl.pixelStore(GL_UNPACK_ROW_LENGTH, p.stride);
gl.pixelStore(GL_UNPACK_ALIGNMENT, p.alignment);
gl.pixelStore(GL_UNPACK_SKIP_PIXELS, p.left);
gl.pixelStore(GL_UNPACK_SKIP_ROWS, p.top);
gl.pixelStore(GL_UNPACK_ROW_LENGTH, GLint(p.stride));
gl.pixelStore(GL_UNPACK_ALIGNMENT, GLint(p.alignment));
gl.pixelStore(GL_UNPACK_SKIP_PIXELS, GLint(p.left));
gl.pixelStore(GL_UNPACK_SKIP_ROWS, GLint(p.top));
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
@@ -1878,7 +1848,7 @@ void OpenGLDriver::setTextureData(GLTexture* t,
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
width, height, glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_3D:
assert_invariant(zoffset + depth <= std::max(1u, t->depth >> level));
@@ -1887,7 +1857,7 @@ void OpenGLDriver::setTextureData(GLTexture* t,
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
width, height, depth, glFormat, glType, p.buffer);
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
assert_invariant(zoffset + depth <= t->depth);
@@ -1897,19 +1867,25 @@ 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 +1908,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 +1942,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 +1951,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;
}
+2 -2
View File
@@ -283,13 +283,13 @@ private:
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& data, FaceOffsets const* faceOffsets);
PixelBufferDescriptor&& p);
void setCompressedTextureData(GLTexture* t,
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& data, FaceOffsets const* faceOffsets);
PixelBufferDescriptor&& p);
void renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width,
uint32_t height, uint8_t samples) const noexcept;
-10
View File
@@ -401,16 +401,6 @@ io::ostream& operator<<(io::ostream& out, const AttributeArray& type) {
return out << "AttributeArray[" << type.max_size() << "]{}";
}
io::ostream& operator<<(io::ostream& out, const FaceOffsets& type) {
return out << "FaceOffsets{"
<< type[0] << ", "
<< type[1] << ", "
<< type[2] << ", "
<< type[3] << ", "
<< type[4] << ", "
<< type[5] << "}";
}
io::ostream& operator<<(io::ostream& out, const RasterState& rs) {
// TODO: implement decoding of enums
return out << "RasterState{"
+1 -16
View File
@@ -937,14 +937,6 @@ void VulkanDriver::resetBufferObject(Handle<HwBufferObject> boh) {
// This is only useful if updateBufferObjectUnsynchronized() is implemented unsynchronizedly.
}
void VulkanDriver::update2DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& data) {
handle_cast<VulkanTexture*>(th)->updateImage(data, width, height, 1,
xoffset, yoffset, 0, level);
scheduleDestroy(std::move(data));
}
void VulkanDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
handle_cast<VulkanTexture*>(th)->setPrimaryRange(minLevel, maxLevel);
}
@@ -959,12 +951,6 @@ void VulkanDriver::update3DImage(
scheduleDestroy(std::move(data));
}
void VulkanDriver::updateCubeImage(Handle<HwTexture> th, uint32_t level,
PixelBufferDescriptor&& data, FaceOffsets faceOffsets) {
handle_cast<VulkanTexture*>(th)->updateCubeImage(data, faceOffsets, level);
scheduleDestroy(std::move(data));
}
void VulkanDriver::setupExternalImage(void* image) {
}
@@ -1957,8 +1943,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
"loadUniformBuffer",
"updateBufferObject",
"updateIndexBuffer",
"update2DImage",
"updateCubeImage",
"update3DImage",
};
static const utils::StaticString BEGIN_COMMAND = "beginRenderPass";
static const utils::StaticString END_COMMAND = "endRenderPass";
+4 -48
View File
@@ -205,7 +205,9 @@ VulkanTexture::~VulkanTexture() {
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
assert_invariant(width <= this->width && height <= this->height && depth <= this->depth);
assert_invariant(width <= this->width && height <= this->height);
assert_invariant(depth <= this->depth * (target == SamplerType::SAMPLER_CUBEMAP ? 6 : 1));
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
@@ -260,7 +262,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
};
// Vulkan specifies subregions for 3D textures differently than from 2D arrays.
if (target == SamplerType::SAMPLER_2D_ARRAY) {
if (target == SamplerType::SAMPLER_2D_ARRAY || target == SamplerType::SAMPLER_CUBEMAP) {
copyRegion.imageOffset.z = 0;
copyRegion.imageExtent.depth = 1;
copyRegion.imageSubresource.baseArrayLayer = zoffset;
@@ -313,52 +315,6 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
transitionLayout(cmdbuffer, range, getDefaultImageLayout(usage));
}
void VulkanTexture::updateCubeImage(const PixelBufferDescriptor& data,
const FaceOffsets& faceOffsets, uint32_t miplevel) {
assert_invariant(this->target == SamplerType::SAMPLER_CUBEMAP);
const bool reshape = getBytesPerPixel(format) == 3;
const void* cpuData = data.buffer;
const uint32_t numSrcBytes = data.size;
const uint32_t numDstBytes = reshape ? (4 * numSrcBytes / 3) : numSrcBytes;
// Create and populate the staging buffer.
VulkanStage const* stage = mStagePool.acquireStage(numDstBytes);
void* mapped;
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
if (reshape) {
DataReshaper::reshape<uint8_t, 3, 4>(mapped, cpuData, numSrcBytes);
} else {
memcpy(mapped, cpuData, numSrcBytes);
}
vmaUnmapMemory(mContext.allocator, stage->memory);
vmaFlushAllocation(mContext.allocator, stage->memory, 0, numDstBytes);
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
const uint32_t width = std::max(1u, this->width >> miplevel);
const uint32_t height = std::max(1u, this->height >> miplevel);
const VkImageSubresourceRange range = { getImageAspect(), miplevel, 1, 0, 6 };
const VkImageLayout textureLayout = getDefaultImageLayout(usage);
transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy regions[6] = {{}};
VkExtent3D extent { width, height, 1 };
for (size_t face = 0; face < 6; face++) {
auto& region = regions[face];
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.baseArrayLayer = face;
region.imageSubresource.layerCount = 1;
region.imageSubresource.mipLevel = miplevel;
region.imageExtent = extent;
region.bufferOffset = faceOffsets.offsets[face];
}
vkCmdCopyBufferToImage(cmdbuffer, stage->buffer, mTextureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6, regions);
transitionLayout(cmdbuffer, range, textureLayout);
}
void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel) {
maxMiplevel = filament::math::min(int(maxMiplevel), int(this->levels - 1));
mPrimaryViewRange.baseMipLevel = minMiplevel;
@@ -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); }
+2 -4
View File
@@ -176,7 +176,6 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
// Draw a circle on a yellow background.
uint32_t* texels = (uint32_t*) buffer0;
FaceOffsets offsets;
for (int face = 0; face < 6; face++) {
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
@@ -187,12 +186,11 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
texels[row * width + col] = toUintColor(float4(color, 1.0f));
}
}
offsets[face] = face * width * height * 4;
texels += offsets[face] / 4;
texels += face * width * height;
}
// Upload to the GPU.
dapi.updateCubeImage(texture, level, std::move(pb), offsets);
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
}
TEST_F(BackendTest, CubemapMinify) {
+1 -1
View File
@@ -168,7 +168,7 @@ TEST_F(BackendTest, FeedbackLoops) {
}
auto cb = [](void* buffer, size_t size, void* user) { free(buffer); };
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
api.update2DImage(texture, 0, 0, 0, kTexWidth, kTexHeight, std::move(pb));
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
for (int frame = 0; frame < kNumFrames; frame++) {
+8 -8
View File
@@ -309,7 +309,7 @@ TEST_F(BackendTest, UpdateImage2D) {
testCases.emplace_back("RGB, UINT_10F_11F_11F_REV -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
testCases.emplace_back("RGB, HALF -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
/* // Test integer format uploads. */
// Test integer format uploads.
// TODO: These cases fail on OpenGL and Vulkan.
testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI);
testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI);
@@ -363,14 +363,14 @@ TEST_F(BackendTest, UpdateImage2D) {
PixelBufferDescriptor subregion2 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
PixelBufferDescriptor subregion3 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
PixelBufferDescriptor subregion4 = checkerboardPixelBuffer(pf, pt, 256, t.bufferPadding);
api.update2DImage(texture, 0, 0, 0, 256, 256, std::move(subregion1));
api.update2DImage(texture, 0, 256, 0, 256, 256, std::move(subregion2));
api.update2DImage(texture, 0, 0, 256, 256, 256, std::move(subregion3));
api.update2DImage(texture, 0, 256, 256, 256, 256, std::move(subregion4));
api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1, std::move(subregion1));
api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1, std::move(subregion2));
api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1, std::move(subregion3));
api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1, std::move(subregion4));
} else {
PixelBufferDescriptor descriptor
= checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding);
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
}
SamplerGroup samplers(1);
@@ -454,7 +454,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
}
}
api.update2DImage(texture, 0, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.beginFrame(0, 0);
@@ -524,7 +524,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
// Create image data.
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
api.update2DImage(texture, 1, 0, 0, 512, 512, std::move(descriptor));
api.update3DImage(texture, 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.beginFrame(0, 0);
+86 -56
View File
@@ -68,6 +68,9 @@ class UTILS_PUBLIC Texture : public FilamentAPI {
public:
static constexpr const size_t BASE_LEVEL = 0;
//! Face offsets for all faces of a cubemap
struct FaceOffsets;
using PixelBufferDescriptor = backend::PixelBufferDescriptor; //!< Geometry of a pixel buffer
using Sampler = backend::SamplerType; //!< Type of sampler
using InternalFormat = backend::TextureFormat; //!< Internal texel format
@@ -75,7 +78,6 @@ public:
using Format = backend::PixelDataFormat; //!< Pixel color format
using Type = backend::PixelDataType; //!< Pixel data format
using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format
using FaceOffsets = backend::FaceOffsets; //!< Cube map faces offsets
using Usage = backend::TextureUsage; //!< Usage affects texel layout
using Swizzle = backend::TextureSwizzle; //!< Texture swizzle
@@ -133,7 +135,8 @@ public:
* effectively create a 3D texture.
* @param depth Depth of the texture in texels (default: 1).
* @return This Builder, for chaining calls.
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_ARRAY or it has no effect.
* @attention This Texture instance must use Sampler::SAMPLER_3D or
* Sampler::SAMPLER_2D_ARRAY or it has no effect.
*/
Builder& depth(uint32_t depth) noexcept;
@@ -289,59 +292,8 @@ public:
InternalFormat getFormat() const noexcept;
/**
* Specify the image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with driver::SamplerType::SAMPLER_2D.
*
* @note
* This is equivalent to calling:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 2D texture for a level.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
* @param xoffset Left offset of the sub-region to update.
* @param yoffset Bottom offset of the sub-region to update.
* @param width Width of the sub-region to update.
* @param height Height of the sub-region to update.
* @param buffer Client-side buffer containing the image to set.
*
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_2D or
* Sampler::SAMPLER_EXTERNAL. IF the later is specified
* and external textures are supported by the driver implementation,
* this method will have no effect, otherwise it will behave as if the
* texture was specified with Sampler::SAMPLER_2D.
*
* @see Builder::sampler()
*/
void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const;
/**
* Updates a sub-image of a 3D texture or 2D texture array for a level.
* Updates a sub-image of a 3D texture or 2D texture array for a level. Cubemaps are treated
* like a 2D array of six layers.
*
* @param engine Engine this texture is associated to.
* @param level Level to set the image for.
@@ -356,7 +308,8 @@ public:
* @attention \p engine must be the instance passed to Builder::build()
* @attention \p level must be less than getLevels().
* @attention \p buffer's Texture::Format must match that of getFormat().
* @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_array.
* @attention This Texture instance must use Sampler::SAMPLER_3D, Sampler::SAMPLER_2D_ARRAY
* or Sampler::SAMPLER_CUBEMAP.
*
* @see Builder::sampler()
*/
@@ -365,6 +318,32 @@ public:
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& buffer) const;
/**
* inline helper to update a 2D texture
*
* @see setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
inline void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const {
setImage(engine, level, 0, 0, 0, getWidth(level), getHeight(level), 1, std::move(buffer));
}
/**
* inline helper to update a 2D texture
*
* @see setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
inline void setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const {
setImage(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer));
}
/**
* Specify all six images of a cube map level.
*
@@ -382,7 +361,13 @@ public:
* @attention This Texture instance must use Sampler::SAMPLER_CUBEMAP or it has no effect
*
* @see Texture::CubemapFace, Builder::sampler()
*
* @deprecated Instead, use setImage(Engine& engine, size_t level,
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
* uint32_t width, uint32_t height, uint32_t depth,
* PixelBufferDescriptor&& buffer)
*/
[[deprecated]]
void setImage(Engine& engine, size_t level,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
@@ -510,6 +495,51 @@ public:
void generatePrefilterMipmap(Engine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* options = nullptr);
/** @deprecated */
struct FaceOffsets {
using size_type = size_t;
union {
struct {
size_type px; //!< +x face offset in bytes
size_type nx; //!< -x face offset in bytes
size_type py; //!< +y face offset in bytes
size_type ny; //!< -y face offset in bytes
size_type pz; //!< +z face offset in bytes
size_type nz; //!< -z face offset in bytes
};
size_type offsets[6];
};
size_type operator[](size_t n) const noexcept { return offsets[n]; }
size_type& operator[](size_t n) { return offsets[n]; }
FaceOffsets() noexcept = default;
explicit FaceOffsets(size_type faceSize) noexcept {
px = faceSize * 0;
nx = faceSize * 1;
py = faceSize * 2;
ny = faceSize * 3;
pz = faceSize * 4;
nz = faceSize * 5;
}
FaceOffsets(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
}
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
return *this;
}
};
};
} // namespace filament
+2 -2
View File
@@ -502,8 +502,8 @@ std::pair<size_t, size_t> Froxelizer::clipToIndices(float2 const& clip) const no
void Froxelizer::commit(backend::DriverApi& driverApi) {
// send data to GPU
driverApi.update2DImage(mFroxelTexture, 0, 0, 0,
FROXEL_BUFFER_WIDTH, FROXEL_BUFFER_HEIGHT,{
driverApi.update3DImage(mFroxelTexture, 0, 0, 0, 0,
FROXEL_BUFFER_WIDTH, FROXEL_BUFFER_HEIGHT, 1, {
mFroxelBufferUser.begin(), mFroxelBufferUser.sizeInBytes(),
PixelBufferDescriptor::PixelDataFormat::RG_INTEGER,
PixelBufferDescriptor::PixelDataType::USHORT });
+2 -20
View File
@@ -266,33 +266,15 @@ void PostProcessManager::init() noexcept {
mStarburstTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::R8, 1, 256, 1, 1, TextureUsage::DEFAULT);
PixelBufferDescriptor dataOne(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataOneArray(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataZero(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataStarburst(driver.allocate(256), 256, PixelDataFormat::R, PixelDataType::UBYTE);
*static_cast<uint32_t *>(dataOne.buffer) = 0xFFFFFFFF;
*static_cast<uint32_t *>(dataOneArray.buffer) = 0xFFFFFFFF;
*static_cast<uint32_t *>(dataZero.buffer) = 0;
std::generate_n((uint8_t*)dataStarburst.buffer, 256,
[&dist = mUniformDistribution, &gen = mEngine.getRandomEngine()]() {
float r = 0.5f + 0.5f * dist(gen);
return uint8_t(r * 255.0f);
});
driver.update2DImage(engine.getOneTexture(),
0, 0, 0, 1, 1,
std::move(dataOne));
driver.update3DImage(engine.getOneTextureArray(),
0, 0, 0, 0, 1, 1, 1,
std::move(dataOneArray));
driver.update2DImage(engine.getZeroTexture(),
0, 0, 0, 1, 1,
std::move(dataZero));
driver.update2DImage(mStarburstTexture,
0, 0, 0, 256, 1,
driver.update3DImage(mStarburstTexture,
0, 0, 0, 0, 256, 1, 1,
std::move(dataStarburst));
}
-12
View File
@@ -45,18 +45,6 @@ Texture::InternalFormat Texture::getFormat() const noexcept {
return upcast(this)->getFormat();
}
void Texture::setImage(Engine& engine, size_t level,
Texture::PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine), level, std::move(buffer));
}
void Texture::setImage(Engine& engine,
size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
PixelBufferDescriptor&& buffer) const {
upcast(this)->setImage(upcast(engine),
level, xoffset, yoffset, width, height, std::move(buffer));
}
void Texture::setImage(Engine& engine, size_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
+8 -18
View File
@@ -345,38 +345,29 @@ void FEngine::init() {
mDummyZeroTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
mDummyOneIntegerTextureArray = driverApi.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
TextureFormat::RGBA8I, 1, 1, 1, 1, TextureUsage::DEFAULT);
mDummyZeroTexture = driverApi.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
// initialize the dummy textures so that their contents are not undefined
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
static const uint32_t zeroes = 0;
static const uint32_t zeroes[6] = {0};
static const uint32_t ones = 0xffffffff;
static const uint32_t signedOnes = 0x7f7f7f7f;
mDefaultIblTexture->setImage(*this, 0,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE), {});
driverApi.update3DImage(mDefaultIblTexture->getHwHandle(), 0, 0, 0, 0, 1, 1, 6,
{ zeroes, sizeof(zeroes), Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update2DImage(mDummyOneTexture, 0, 0, 0, 1, 1,
PixelBufferDescriptor(&ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
driverApi.update3DImage(mDummyOneIntegerTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&signedOnes, 4, Texture::Format::RGBA_INTEGER, Texture::Type::BYTE));
{ &ones, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
PixelBufferDescriptor(&zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE));
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
mDefaultRenderTarget = driverApi.createDefaultRenderTarget();
@@ -467,7 +458,6 @@ void FEngine::shutdown() {
driver.destroyTexture(mDummyOneTexture);
driver.destroyTexture(mDummyOneTextureArray);
driver.destroyTexture(mDummyOneIntegerTextureArray);
driver.destroyTexture(mDummyZeroTexture);
driver.destroyTexture(mDummyZeroTextureArray);
-2
View File
@@ -353,7 +353,6 @@ public:
backend::Handle<backend::HwTexture> getZeroTexture() const { return mDummyZeroTexture; }
backend::Handle<backend::HwTexture> getOneTextureArray() const { return mDummyOneTextureArray; }
backend::Handle<backend::HwTexture> getZeroTextureArray() const { return mDummyZeroTextureArray; }
backend::Handle<backend::HwTexture> getOneIntegerTextureArray() const { return mDummyOneIntegerTextureArray; }
static constexpr const size_t MiB = 1024u * 1024u;
size_t getMinCommandBufferSize() const noexcept { return mConfig.minCommandBufferSizeMB * MiB; }
@@ -470,7 +469,6 @@ private:
backend::Handle<backend::HwTexture> mDummyOneTexture;
backend::Handle<backend::HwTexture> mDummyOneTextureArray;
backend::Handle<backend::HwTexture> mDummyZeroTextureArray;
backend::Handle<backend::HwTexture> mDummyOneIntegerTextureArray;
backend::Handle<backend::HwTexture> mDummyZeroTexture;
std::thread::id mMainThreadId{};
+35 -90
View File
@@ -40,6 +40,16 @@ namespace filament {
using namespace backend;
using namespace math;
// this is a hack to be able to create a std::function<> with a non-copyable closure
template<class F>
auto make_copyable_function(F&& f) {
using dF = std::decay_t<F>;
auto spf = std::make_shared<dF>(std::forward<F>(f));
return [spf](auto&& ... args) -> decltype(auto) {
return (*spf)(decltype(args)(args)...);
};
}
struct Texture::BuilderDetails {
intptr_t mImportedId = 0;
uint32_t mWidth = 1;
@@ -180,76 +190,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 +197,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;
}
};
@@ -321,6 +261,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 +269,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 +302,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 +447,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 +618,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()]() {}));
+1 -6
View File
@@ -48,17 +48,12 @@ public:
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;
+1 -1
View File
@@ -64,7 +64,7 @@ private:
bool loadCubemapLevel(filament::Texture** texture,
filament::Texture::PixelBufferDescriptor* outBuffer,
filament::Texture::FaceOffsets* outOffsets,
uint32_t* dim,
const utils::Path& path,
size_t level = 0, std::string const& levelPrefix = "") const;
+6 -9
View File
@@ -196,10 +196,10 @@ bool IBL::loadFromDirectory(const utils::Path& path) {
bool IBL::loadCubemapLevel(filament::Texture** texture, const utils::Path& path, size_t level,
std::string const& levelPrefix) const {
Texture::FaceOffsets offsets;
uint32_t dim;
Texture::PixelBufferDescriptor buffer;
if (loadCubemapLevel(texture, &buffer, &offsets, path, level, levelPrefix)) {
(*texture)->setImage(mEngine, level, std::move(buffer), offsets);
if (loadCubemapLevel(texture, &buffer, &dim, path, level, levelPrefix)) {
(*texture)->setImage(mEngine, level, 0, 0, dim, dim, 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;
}
+14 -9
View File
@@ -76,25 +76,30 @@ Texture* createTexture(Engine* engine, const Ktx1Bundle& ktx, bool srgb,
if (isCompressed(ktxinfo)) {
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
ktx.getBlob({ level, 0, 0 }, &data, &size);
const uint32_t dim = texture->getWidth(level);
texture->setImage(*engine, level, 0, 0, dim, dim, {
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, dim, dim, {
data, size * 6, dataformat, datatype, cb, cbuser
});
}
return texture;
}