Metal: implement lod clamping to fix IBL bug (#2556)

This commit is contained in:
Ben Doherty
2020-05-19 15:40:07 -07:00
committed by GitHub
parent 15e2447cb5
commit 97c9ad7599
6 changed files with 60 additions and 18 deletions

View File

@@ -179,11 +179,13 @@ void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args) {
[encoder setFragmentTexture:args.source.depth atIndex:1];
}
SamplerParams samplerState{
SamplerState s {
.samplerParams = {
.filterMag = args.filter,
.filterMin = static_cast<SamplerMinFilter>(args.filter)
}
};
id<MTLSamplerState> sampler = mContext.samplerStateCache.getOrCreateState(samplerState);
id<MTLSamplerState> sampler = mContext.samplerStateCache.getOrCreateState(s);
[encoder setFragmentSamplerState:sampler atIndex:0];
MTLViewport viewport;

View File

@@ -214,6 +214,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto colorTexture = handle_cast<MetalTexture>(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<HwRenderTarget> rth,
auto depthTexture = handle_cast<MetalTexture>(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<HwTexture> th) {
id <MTLBlitCommandEncoder> 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<HwRenderPrimitive> rph)
texturesToBind[binding] = getOrCreateEmptyTexture(mContext);
}
id <MTLSamplerState> samplerState = mContext->samplerStateCache.getOrCreateState(sampler->s);
SamplerState s {
.samplerParams = sampler->s,
.minLod = metalTexture->minLod,
.maxLod = metalTexture->maxLod
};
id <MTLSamplerState> samplerState = mContext->samplerStateCache.getOrCreateState(s);
samplersToBind[binding] = samplerState;
});

View File

@@ -130,6 +130,7 @@ struct MetalTexture : public HwTexture {
uint32_t height, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor& data, id<MTLBlitCommandEncoder> blitCommandEncoder,
id<MTLCommandBuffer> 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 {

View File

@@ -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<MTLTexture> color, TargetInfo colorInfo, id<MTLTexture> depth,
TargetInfo depthInfo) : HwRenderTarget(width, height), context(context), samples(samples),

View File

@@ -311,13 +311,29 @@ using UniformBufferStateTracker = StateTracker<UniformBufferState>;
// Sampler states
struct SamplerStateCreator {
id<MTLSamplerState> operator()(id<MTLDevice> 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<backend::SamplerParams, id<MTLSamplerState>,
SamplerStateCreator>;
static_assert(sizeof(SamplerState) == 12, "SamplerState unexpected size.");
struct SamplerStateCreator {
id<MTLSamplerState> operator()(id<MTLDevice> device, const SamplerState& state) noexcept;
};
using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerStateCreator>;
// Raster-related state

View File

@@ -99,18 +99,21 @@ id<MTLDepthStencilState> DepthStateCreator::operator()(id<MTLDevice> device,
}
id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> 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];
}