Skip to content

Commit

Permalink
Implement setMinMaxLevels for Metal (#7158)
Browse files Browse the repository at this point in the history
  • Loading branch information
bejado authored Sep 13, 2023
1 parent ec30ddd commit 2114995
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 200 deletions.
1 change: 1 addition & 0 deletions filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ if (APPLE)
test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
test/test_MipLevels.cpp
)

target_link_libraries(backend_test PRIVATE
Expand Down
42 changes: 11 additions & 31 deletions filament/backend/src/metal/MetalDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
"Color texture passed to render target has no texture allocation");
colorTexture->updateLodRange(buffer.level);
colorTexture->extendLodRangeTo(buffer.level);
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
}

Expand All @@ -356,7 +356,7 @@
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
"Depth texture passed to render target has no texture allocation.");
depthTexture->updateLodRange(depth.level);
depthTexture->extendLodRangeTo(depth.level);
depthAttachment = { depthTexture, depth.level, depth.layer };
}

Expand All @@ -367,7 +367,7 @@
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
"Stencil texture passed to render target has no texture allocation.");
stencilTexture->updateLodRange(stencil.level);
stencilTexture->extendLodRangeTo(stencil.level);
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
}

Expand Down Expand Up @@ -789,6 +789,8 @@
}

void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
auto tex = handle_cast<MetalTexture>(th);
tex->setLodRange(minLevel, maxLevel);
}

void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
Expand Down Expand Up @@ -900,14 +902,13 @@
// 2. LOD-clamped textures
//
// Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into
// the argument buffer representing the sampler group. So, we bind what we can now and wait
// until draw call time to bind any special cases (done in finalizeSamplerGroup).
// the argument buffer representing the sampler group. So, we wait until draw call time to bind
// textures (done in finalizeSamplerGroup).
// The good news is that once a render pass has started, the texture bindings won't change.
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call.
// Even if we do know all the final textures at this point, we still wait until draw call time
// to call finalizeSamplerGroup, which has one additional responsibility: to call useResources
// for all the textures, which is required by Metal.
// finalizeSamplerGroup has one additional responsibility: to call useResources for all the
// textures, which is required by Metal.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) {
// Assign a default texture / sampler to empty slots.
Expand All @@ -930,27 +931,6 @@
sb->setFinalizedSampler(s, sampler);

sb->setTextureHandle(s, samplers[s].t);

auto* t = handle_cast<MetalTexture>(samplers[s].t);
assert_invariant(t);

// If this texture is an external texture, we defer binding the texture until draw call time
// (in finalizeSamplerGroup).
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
continue;
}

if (!t->allLodsValid()) {
// The texture doesn't have all of its LODs loaded, and this could change by the time we
// issue a draw call with this sampler group. So, we defer binding the texture until
// draw call time (in finalizeSamplerGroup).
continue;
}

// If we get here, we know we have a valid MTLTexture that's guaranteed not to change.
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
assert_invariant(mtlTexture);
sb->setFinalizedTexture(s, mtlTexture);
}

scheduleDestroy(std::move(data));
Expand Down Expand Up @@ -1376,8 +1356,8 @@
}

void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind any textures that were unable to be bound in updateSamplerGroup.
// All the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind all the textures.

id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);

Expand Down
15 changes: 4 additions & 11 deletions filament/backend/src/metal/MetalHandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,14 @@ class MetalTexture : public HwTexture {
void generateMipmaps() noexcept;

// A texture starts out with none of its mip levels (also referred to as LODs) available for
// reading. 3 actions update the range of LODs available:
// reading. 4 actions update the range of LODs available:
// - calling loadImage
// - calling generateMipmaps
// - using the texture as a render target attachment
// The range of available mips can only increase, never decrease.
// - calling setMinMaxLevels
// A texture's available mips are consistent throughout a render pass.
void updateLodRange(uint32_t level);
void updateLodRange(uint32_t minLevel, uint32_t maxLevel);

// Returns true if the texture has all of its mip levels accessible for reading.
// For any MetalTexture, once this is true, will always return true.
// The value returned will remain consistent for an entire render pass.
bool allLodsValid() const {
return minLod == 0 && maxLod == levels - 1;
}
void setLodRange(uint32_t minLevel, uint32_t maxLevel);
void extendLodRangeTo(uint32_t level);

static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);

Expand Down
14 changes: 7 additions & 7 deletions filament/backend/src/metal/MetalHandles.mm
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ void presentDrawable(bool presentFrame, void* user) {
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
externalImage(context) {
texture = metalTexture;
updateLodRange(0, levels - 1);
setLodRange(0, levels - 1);
}

MetalTexture::~MetalTexture() {
Expand Down Expand Up @@ -658,14 +658,14 @@ void presentDrawable(bool presentFrame, void* user) {
}
}

updateLodRange(level);
extendLodRangeTo(level);
}

void MetalTexture::generateMipmaps() noexcept {
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:texture];
[blitEncoder endEncoding];
updateLodRange(0, texture.mipmapLevelCount - 1);
setLodRange(0, texture.mipmapLevelCount - 1);
}

void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
Expand Down Expand Up @@ -788,18 +788,18 @@ void presentDrawable(bool presentFrame, void* user) {
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
}

void MetalTexture::updateLodRange(uint32_t level) {
void MetalTexture::extendLodRangeTo(uint32_t level) {
assert_invariant(!isInRenderPass(&context));
minLod = std::min(minLod, level);
maxLod = std::max(maxLod, level);
lodTextureView = nil;
}

void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
void MetalTexture::setLodRange(uint32_t min, uint32_t max) {
assert_invariant(!isInRenderPass(&context));
assert_invariant(min <= max);
minLod = std::min(minLod, min);
maxLod = std::max(maxLod, max);
minLod = min;
maxLod = max;
lodTextureView = nil;
}

Expand Down
61 changes: 0 additions & 61 deletions filament/backend/test/BackendTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,66 +211,5 @@ int runTests() {
return RUN_ALL_TESTS();
}

void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) {
assert_invariant(type != PixelDataType::COMPRESSED);
switch (format) {
case PixelDataFormat::UNUSED:
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
outComponents = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
outComponents = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
outComponents = 3;
break;
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
outComponents = 4;
break;
}

outBpp = outComponents;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
outBpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
outBpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
outBpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 2;
break;
}
}


} // namespace test

6 changes: 0 additions & 6 deletions filament/backend/test/BackendTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ class BackendTest : public ::testing::Test {
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
};


// Utilities

void getPixelInfo(filament::backend::PixelDataFormat format, filament::backend::PixelDataType type,
size_t& outComponents, int& outBpp);

} // namespace test

#endif
Loading

0 comments on commit 2114995

Please sign in to comment.