From 3ecae611891ac3a8582cc4cfa38991a9de3008c7 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 16 Jan 2022 14:18:34 -0400 Subject: [PATCH] metal: improve texture initialization. --- src/modules/graphics/metal/Buffer.mm | 3 + src/modules/graphics/metal/Graphics.mm | 5 ++ src/modules/graphics/metal/Texture.mm | 108 +++++++++++++++++++------ 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/src/modules/graphics/metal/Buffer.mm b/src/modules/graphics/metal/Buffer.mm index b860ce358..b4ac8ef5b 100644 --- a/src/modules/graphics/metal/Buffer.mm +++ b/src/modules/graphics/metal/Buffer.mm @@ -78,6 +78,9 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id device, const Settin // TODO: minimumTextureBufferAlignmentForPixelFormat MTLPixelFormat pixformat = getMTLPixelFormat(getDataMember(0).decl.format); + if (pixformat == MTLPixelFormatInvalid) + throw love::Exception("Could not create Metal texel buffer: invalid format."); + size_t width = arraylength * getDataMembers().size(); auto desc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat:pixformat width:width diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index cdbcf6f34..d680c25a9 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -2062,6 +2062,11 @@ Graphics::RendererInfo Graphics::getRendererInfo() const int Graphics::getClosestMSAASamples(int requestedsamples) { + // We currently rely on StoreAndMultisampleResolve (unfortunately), which + // isn't supported by old phone GPUs. + if (!families.apple[3] && !families.mac[1] && !families.macCatalyst[1]) + return 1; + const int checkmsaa[] = {32, 16, 8, 4, 2}; for (int samples : checkmsaa) { diff --git a/src/modules/graphics/metal/Texture.mm b/src/modules/graphics/metal/Texture.mm index 72d6a3962..f09a73f37 100644 --- a/src/modules/graphics/metal/Texture.mm +++ b/src/modules/graphics/metal/Texture.mm @@ -105,43 +105,103 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id device, const int mipcount = getMipmapCount(); - int slicecount = 1; - if (texType == TEXTURE_VOLUME) - slicecount = getDepth(); - else if (texType == TEXTURE_2D_ARRAY) - slicecount = getLayerCount(); - else if (texType == TEXTURE_CUBE) - slicecount = 6; + bool cangeneratemips = true; + if (isPixelFormatDepthStencil(format) || isPixelFormatCompressed(format)) + cangeneratemips = false; + + // generateMipmapsForTexture is only supported for color renderable + + // filterable formats. + uint32 genmipsflags = PIXELFORMATUSAGEFLAGS_LINEAR | PIXELFORMATUSAGEFLAGS_RENDERTARGET; + if (!gfx->isPixelFormatSupported(format, (PixelFormatUsageFlags) genmipsflags)) + cangeneratemips = false; + + bool shouldgeneratemips = false; + + std::vector emptydata; + MTLRenderPassDescriptor *passdesc = nil; + + // Initialize texture. for (int mip = 0; mip < mipcount; mip++) { - for (int slice = 0; slice < slicecount; slice++) + for (int slice = 0; slice < getSliceCount(mip); slice++) { auto imgd = data != nullptr ? data->get(slice, mip) : nullptr; if (imgd != nullptr) + { uploadImageData(imgd, mip, slice, 0, 0); - } - } + } + else if (mip > 0 && cangeneratemips) + { + // Handled in the generateMipmaps call below. + shouldgeneratemips = true; + continue; + } + else if (getMSAA() <= 1 && !isPixelFormatDepthStencil(format) && !isPixelFormatCompressed(format)) + { + // Initialize to transparent black. + if (emptydata.empty()) + emptydata.resize(getPixelFormatSliceSize(format, w, h)); - if (data == nullptr || data->get(0, 0) == nullptr) - { - // Initialize all slices to transparent black. - if (!isPixelFormatDepthStencil(format)) - { - std::vector emptydata(getPixelFormatSliceSize(format, w, h)); - Rect r = {0, 0, w, h}; - for (int i = 0; i < slicecount; i++) - uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r); - } - else - { - // TODO + Rect r = {0, 0, getPixelWidth(mip), getPixelHeight(mip)}; + uploadByteData(format, emptydata.data(), emptydata.size(), mip, slice, r); + } + else if (isRenderTarget()) + { + // Clear to transparent black. + gfx->submitAllEncoders(Graphics::SUBMIT_STORE); + id cmd = gfx->useCommandBuffer(); + + if (passdesc == nil) + passdesc = [MTLRenderPassDescriptor renderPassDescriptor]; + + auto configattachment = [&](MTLRenderPassAttachmentDescriptor *attachment) + { + attachment.texture = texture; + attachment.level = mip; + attachment.slice = texType == TEXTURE_VOLUME ? 0 : slice; + attachment.depthPlane = texType == TEXTURE_VOLUME ? slice : 0; + attachment.loadAction = MTLLoadActionClear; + attachment.storeAction = MTLStoreActionStore; + + if (actualMSAASamples > 1) + { + attachment.texture = msaaTexture; + attachment.resolveTexture = texture; + attachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; + } + }; + + if (isPixelFormatDepth(format)) + { + configattachment(passdesc.depthAttachment); + passdesc.depthAttachment.clearDepth = 1.0; + } + if (isPixelFormatStencil(format)) + { + configattachment(passdesc.stencilAttachment); + passdesc.stencilAttachment.clearStencil = 0; + } + if (!isPixelFormatDepthStencil(format)) + { + configattachment(passdesc.colorAttachments[0]); + passdesc.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0); + } + + id encoder = [cmd renderCommandEncoderWithDescriptor:passdesc]; + [encoder endEncoding]; + } + else + { + // Shouldn't be possible to get here. + throw love::Exception("Could not initialize texture to transparent black."); + } } } // Non-readable textures can't have mipmaps (enforced in the base class), // so generateMipmaps here is fine - when they aren't already initialized. - if (getMipmapCount() > 1 && (data == nullptr || data->getMipmapCount() <= 1)) + if (shouldgeneratemips) generateMipmaps(); setSamplerState(samplerState);