diff --git a/filament/backend/src/metal/MetalBlitter.mm b/filament/backend/src/metal/MetalBlitter.mm index 7b9db2144c..8d2117e825 100644 --- a/filament/backend/src/metal/MetalBlitter.mm +++ b/filament/backend/src/metal/MetalBlitter.mm @@ -179,11 +179,13 @@ void MetalBlitter::blit(id cmdBuffer, const BlitArgs& args) { [encoder setFragmentTexture:args.source.depth atIndex:1]; } - SamplerParams samplerState{ + SamplerState s { + .samplerParams = { .filterMag = args.filter, .filterMin = static_cast(args.filter) + } }; - id sampler = mContext.samplerStateCache.getOrCreateState(samplerState); + id sampler = mContext.samplerStateCache.getOrCreateState(s); [encoder setFragmentSamplerState:sampler atIndex:0]; MTLViewport viewport; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 4c203cd13e..357ceff3a2 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -214,6 +214,7 @@ void MetalDriver::createRenderTargetR(Handle rth, auto colorTexture = handle_cast(mHandleMap, color[0].handle); ASSERT_PRECONDITION(colorTexture->texture, "Color texture passed to render target has no texture allocation"); + colorTexture->updateLodRange(color[0].level); return colorTexture->texture; } else if (any(targetBufferFlags & TargetBufferFlags::COLOR)) { ASSERT_POSTCONDITION(false, "The COLOR flag was specified, but no color texture provided."); @@ -226,6 +227,7 @@ void MetalDriver::createRenderTargetR(Handle rth, auto depthTexture = handle_cast(mHandleMap, depth.handle); ASSERT_PRECONDITION(depthTexture->texture, "Depth texture passed to render target has no texture allocation."); + depthTexture->updateLodRange(depth.level); return depthTexture->texture; } else if (any(targetBufferFlags & TargetBufferFlags::DEPTH)) { ASSERT_POSTCONDITION(false, "The DEPTH flag was specified, but no depth texture provided."); @@ -664,6 +666,8 @@ void MetalDriver::generateMipmaps(Handle th) { id blitEncoder = [getPendingCommandBuffer(mContext) blitCommandEncoder]; [blitEncoder generateMipmapsForTexture:tex->texture]; [blitEncoder endEncoding]; + tex->minLod = 0; + tex->maxLod = tex->texture.mipmapLevelCount - 1; } bool MetalDriver::canGenerateMipmaps() { @@ -1080,7 +1084,12 @@ void MetalDriver::draw(backend::PipelineState ps, Handle rph) texturesToBind[binding] = getOrCreateEmptyTexture(mContext); } - id samplerState = mContext->samplerStateCache.getOrCreateState(sampler->s); + SamplerState s { + .samplerParams = sampler->s, + .minLod = metalTexture->minLod, + .maxLod = metalTexture->maxLod + }; + id samplerState = mContext->samplerStateCache.getOrCreateState(s); samplersToBind[binding] = samplerState; }); diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index eccf584af3..e68b5b0ef3 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -130,6 +130,7 @@ struct MetalTexture : public HwTexture { uint32_t height, uint32_t byteOffset, uint32_t slice, PixelBufferDescriptor& data, id blitCommandEncoder, id blitCommandBuffer) noexcept; + void updateLodRange(uint32_t level); MetalContext& context; MetalExternalImage externalImage; @@ -139,6 +140,8 @@ struct MetalTexture : public HwTexture { uint8_t blockHeight; // The number of vertical pixels per block (only for compressed texture formats). TextureReshaper reshaper; MTLPixelFormat metalPixelFormat; + uint32_t minLod = UINT_MAX; + uint32_t maxLod = 0; }; struct MetalSamplerGroup : public HwSamplerGroup { diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 73264f2208..5f7cc813e5 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -303,6 +303,8 @@ void MetalTexture::load2DImage(uint32_t level, uint32_t xoffset, uint32_t yoffse loadSlice(level, xoffset, yoffset, width, height, 0, 0, data, blitCommandEncoder, blitCommandBuffer); + updateLodRange(level); + [blitCommandEncoder endEncoding]; } @@ -321,6 +323,8 @@ void MetalTexture::loadCubeImage(const FaceOffsets& faceOffsets, int miplevel, blitCommandBuffer); } + updateLodRange((uint32_t) miplevel); + [blitCommandEncoder endEncoding]; } @@ -407,6 +411,11 @@ void MetalTexture::loadSlice(uint32_t level, uint32_t xoffset, uint32_t yoffset, } } +void MetalTexture::updateLodRange(uint32_t level) { + minLod = std::min(minLod, level); + maxLod = std::max(maxLod, level); +} + MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples, id color, TargetInfo colorInfo, id depth, TargetInfo depthInfo) : HwRenderTarget(width, height), context(context), samples(samples), diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index a7246c53b7..4f13e9f8dd 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -311,13 +311,29 @@ using UniformBufferStateTracker = StateTracker; // Sampler states -struct SamplerStateCreator { - id operator()(id device, const backend::SamplerParams& state) - noexcept; +struct SamplerState { + backend::SamplerParams samplerParams; + uint32_t minLod = 0; + uint32_t maxLod = UINT_MAX; + + bool operator==(const SamplerState& rhs) const noexcept { + return this->samplerParams == rhs.samplerParams && + this->minLod == rhs.minLod && + this->maxLod == rhs.maxLod; + } + + bool operator!=(const SamplerState& rhs) const noexcept { + return !operator==(rhs); + } }; -using SamplerStateCache = StateCache, - SamplerStateCreator>; +static_assert(sizeof(SamplerState) == 12, "SamplerState unexpected size."); + +struct SamplerStateCreator { + id operator()(id device, const SamplerState& state) noexcept; +}; + +using SamplerStateCache = StateCache, SamplerStateCreator>; // Raster-related state diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index dcd4026d73..807d573096 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -99,18 +99,21 @@ id DepthStateCreator::operator()(id device, } id SamplerStateCreator::operator()(id device, - const backend::SamplerParams& state) noexcept { + const SamplerState& state) noexcept { + backend::SamplerParams params = state.samplerParams; MTLSamplerDescriptor* samplerDescriptor = [MTLSamplerDescriptor new]; - samplerDescriptor.minFilter = getFilter(state.filterMin); - samplerDescriptor.magFilter = getFilter(state.filterMag); - samplerDescriptor.mipFilter = getMipFilter(state.filterMin); - samplerDescriptor.sAddressMode = getAddressMode(state.wrapS); - samplerDescriptor.tAddressMode = getAddressMode(state.wrapT); - samplerDescriptor.rAddressMode = getAddressMode(state.wrapR); - samplerDescriptor.maxAnisotropy = 1u << state.anisotropyLog2; + samplerDescriptor.minFilter = getFilter(params.filterMin); + samplerDescriptor.magFilter = getFilter(params.filterMag); + samplerDescriptor.mipFilter = getMipFilter(params.filterMin); + samplerDescriptor.sAddressMode = getAddressMode(params.wrapS); + samplerDescriptor.tAddressMode = getAddressMode(params.wrapT); + samplerDescriptor.rAddressMode = getAddressMode(params.wrapR); + samplerDescriptor.maxAnisotropy = 1u << params.anisotropyLog2; + samplerDescriptor.lodMaxClamp = (float) state.maxLod; + samplerDescriptor.lodMinClamp = (float) state.minLod; samplerDescriptor.compareFunction = - state.compareMode == SamplerCompareMode::NONE ? - MTLCompareFunctionNever : getCompareFunction(state.compareFunc); + params.compareMode == SamplerCompareMode::NONE ? + MTLCompareFunctionNever : getCompareFunction(params.compareFunc); return [device newSamplerStateWithDescriptor:samplerDescriptor]; }