Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement setMinMaxLevels for Metal #7158

Merged
merged 2 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading