Metal: implement lod clamping to fix IBL bug (#2556)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user