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:
committed by
Mathias Agopian
parent
815d202f6d
commit
61fb5a588e
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -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()]() {}));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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, 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user