Skip to content

Commit

Permalink
[Metal] Fixed feature set validation for texture view swizzle in Meta…
Browse files Browse the repository at this point in the history
…l backend.

Function to create a MTLTexture view with component swizzling is available in macOS 10.15 and iOS 13.0,
but it requires GPU family Metal3, which is only available in macOS 13.0 and iOS 16.0 or later.

Also report loaded images in Testbed when verbose mode is enabled.
  • Loading branch information
LukasBanana committed Jun 7, 2024
1 parent a335b63 commit 8f600dc
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 21 deletions.
2 changes: 1 addition & 1 deletion sources/Renderer/Metal/MTFeatureSet.mm
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ void LoadFeatureSetCaps(id<MTLDevice> device, MTLFeatureSet fset, RenderingCapab
features.hasMultiSampleTextures = true;
features.hasMultiSampleArrayTextures = false;
features.hasTextureViews = true;
features.hasTextureViewSwizzle = LLGL_OSX_AVAILABLE(macOS 10.15, iOS 13.0, *);
features.hasTextureViewSwizzle = LLGL_OSX_AVAILABLE(macOS 13.0, iOS 16.0, *);
features.hasTextureViewFormatSwizzle = true;
features.hasBufferViews = true;
features.hasConstantBuffers = true;
Expand Down
56 changes: 37 additions & 19 deletions sources/Renderer/Metal/RenderState/MTResourceHeap.mm
Original file line number Diff line number Diff line change
Expand Up @@ -580,21 +580,27 @@ static MTShaderStage StageFlagsToMTShaderStage(long stage)
MTRESOURCEHEAP_DATA0_MTLSAMPLERSTATE(heapPtr)[binding.descriptorIndex] = samplerMT->GetNative();
}

static void ValidateTexViewNoSwizzle(MTTexture& /*textureMT*/, const TextureViewDescriptor& desc)
[[noreturn]]
static void ErrTextureViewSwizzleNotSupported()
{
LLGL_TRAP("cannot create texture-view with swizzling for this version of the Metal API");
}

static void ValidateTexViewNoSwizzle(const TextureViewDescriptor& desc)
{
if (!IsTextureSwizzleIdentity(desc.swizzle))
throw std::runtime_error("cannot create texture-view with swizzling for this version of the Metal API");
ErrTextureViewSwizzleNotSupported();
}

static void ValidateTexViewNoTypeAndRange(MTTexture& textureMT, const TextureViewDescriptor& desc)
static void ValidateTexViewNoTypeAndRange(const TextureViewDescriptor& desc, MTTexture& textureMT)
{
id<MTLTexture> tex = textureMT.GetNative();
if (MTTypes::ToMTLTextureType(desc.type) != [tex textureType])
throw std::runtime_error("cannot create texture-view of different type for this version of the Metal API");
LLGL_TRAP("cannot create texture-view of different type for this version of the Metal API");
if (desc.subresource.baseMipLevel != 0 || desc.subresource.numMipLevels != [tex mipmapLevelCount])
throw std::runtime_error("cannot create texture-view of different MIP-level range for this version of the Metal API");
LLGL_TRAP("cannot create texture-view of different MIP-level range for this version of the Metal API");
if (desc.subresource.baseArrayLayer != 0 || desc.subresource.numArrayLayers != [tex arrayLength])
throw std::runtime_error("cannot create texture-view of different array-layer range for this version of the Metal API");
LLGL_TRAP("cannot create texture-view of different array-layer range for this version of the Metal API");
}

void MTResourceHeap::ExchangeTextureView(
Expand Down Expand Up @@ -624,21 +630,32 @@ static void ValidateTexViewNoTypeAndRange(MTTexture& textureMT, const TextureVie
const auto& subresource = textureViewDesc.subresource;
id<MTLTexture> textureView = nil;

if (@available(macOS 10.15, iOS 13.0, *))
/* Try different methods to create a texture view as some might return NIL even though the host system should support it */
if (!IsTextureSwizzleIdentity(textureViewDesc.swizzle))
{
MTLTextureSwizzleChannels swizzle;
MTTypes::Convert(swizzle, textureViewDesc.swizzle);
textureView = [textureMT.GetNative()
newTextureViewWithPixelFormat: MTTypes::ToMTLPixelFormat(textureViewDesc.format)
textureType: MTTypes::ToMTLTextureType(textureViewDesc.type)
levels: NSMakeRange(subresource.baseMipLevel, subresource.numMipLevels)
slices: NSMakeRange(subresource.baseArrayLayer, subresource.numArrayLayers)
swizzle: swizzle
];
/*
This method is available in macOS 10.15 and iOS 13.0,
but it is only supported by GPU family MTLGPUFamilyMetal3 or later,
which is only available in macOS 13.0 and iOS 16.0 or later.
*/
if (@available(macOS 13.0, iOS 16.0, *))
{
MTLTextureSwizzleChannels swizzle;
MTTypes::Convert(swizzle, textureViewDesc.swizzle);
textureView = [textureMT.GetNative()
newTextureViewWithPixelFormat: MTTypes::ToMTLPixelFormat(textureViewDesc.format)
textureType: MTTypes::ToMTLTextureType(textureViewDesc.type)
levels: NSMakeRange(subresource.baseMipLevel, subresource.numMipLevels)
slices: NSMakeRange(subresource.baseArrayLayer, subresource.numArrayLayers)
swizzle: swizzle
];
}
else
ErrTextureViewSwizzleNotSupported();
}
else if (@available(macOS 10.11, iOS 9.0, *))
{
ValidateTexViewNoSwizzle(textureMT, textureViewDesc);
ValidateTexViewNoSwizzle(textureViewDesc);
textureView = [textureMT.GetNative()
newTextureViewWithPixelFormat: MTTypes::ToMTLPixelFormat(textureViewDesc.format)
textureType: MTTypes::ToMTLTextureType(textureViewDesc.type)
Expand All @@ -648,14 +665,15 @@ static void ValidateTexViewNoTypeAndRange(MTTexture& textureMT, const TextureVie
}
else
{
ValidateTexViewNoSwizzle(textureMT, textureViewDesc);
ValidateTexViewNoTypeAndRange(textureMT, textureViewDesc);
ValidateTexViewNoSwizzle(textureViewDesc);
ValidateTexViewNoTypeAndRange(textureViewDesc, textureMT);
textureView = [textureMT.GetNative()
newTextureViewWithPixelFormat: MTTypes::ToMTLPixelFormat(textureViewDesc.format)
];
}

/* Store texture view reference */
LLGL_ASSERT(textureView != nil, "unable to create Metal texture view");
ExchangeTextureView(descriptorSet, binding, textureView);
return textureView;
}
Expand Down
14 changes: 13 additions & 1 deletion tests/Testbed/TestbedContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -980,15 +980,27 @@ bool TestbedContext::LoadTextures()
{
auto LoadTextureFromFile = [this](const char* name, const std::string& filename) -> Texture*
{
auto PrintLoadingInfo = [&filename]()
{
Log::Printf("Loading image: %s", filename.c_str());
};

if (opt.verbose)
PrintLoadingInfo();

// Load image
int w = 0, h = 0, c = 0;
stbi_uc* imgBuf = stbi_load(filename.c_str(), &w, &h, &c, 4);

if (!imgBuf)
{
Log::Errorf("Failed to load image: %s\n", filename.c_str());
if (!opt.verbose)
PrintLoadingInfo();
Log::Printf(" [ %s ]:\n", TestResultToStr(TestResult::FailedErrors));
return nullptr;
}
else if (opt.verbose)
Log::Printf(" [ Ok ]\n");

// Create texture
ImageView imageView;
Expand Down

0 comments on commit 8f600dc

Please sign in to comment.