Rendering final content on screen for Metal

421 views
Skip to first unread message

Anmol Sud

unread,
Mar 31, 2021, 2:30:50 PM3/31/21
to skia-discuss
I am using the following code to create my Metal Surface.

        MTLTextureDescriptor* desc = [MTLTextureDescriptor new];

        desc.usage = MTLTextureUsageRenderTarget | MTLTextureUsagePixelFormatView;

        desc.pixelFormat = MTLPixelFormatRGBA8Unorm;

        desc.width = (NSUInteger)m_width;

        desc.height = (NSUInteger)m_height;

        desc.textureType = MTLTextureType2D;


        id<MTLDevice> device = static_cast<id<MTLDevice>>(mtlContext.fDevice.get());

        m_surfaceTexture = [device newTextureWithDescriptor:desc];


        GrMtlTextureInfo info;

        info.fTexture = sk_cfp<const void*>(m_surfaceTexture);

        GrBackendRenderTarget target(m_width, m_height, info);


        m_gpuSurface = (SkSurface::MakeFromBackendRenderTarget(m_grContext.get(), target, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, &props));


Notice that the texture format that I use here is "MTLPixelFormatRGBA8Unorm", I want to use "MTLPixelFormatBGRA8Unorm", but apparently that is not supported and returns a NULL surface. "MTLPixelFormatRGBA8Unorm" is not supported by the CAMetalLayer, so my CAMetalLayer still remains on "MTLPixelFormatBGRA8Unorm". So, once the skia rendering is over I have to take this new texture I created and use a simple rendering pipeline to swap the colour channels. Is there a better way to do this?

I see a similar "SkSurface::MakeFromCAMetalLayer", as I understand, it takes the drawable during initialisation, extracts it's texture and directly renders on it. But I do not see a way that I can reuse the same surface across multiple frames since this does not query the "nextDrawable" during consecutive draws. Also, as I remember Apple suggests holding "CAMetalDrawable" instances for the lowest possible time, if we are going to hold a drawable through out the render phase, I believe this should negatively impact performance.

Is there something missing in my understanding? Does skia have any inbuilt functionality that can show the rendered texture on the screen? Is there a better way to do it?

Brian Salomon

unread,
Apr 1, 2021, 8:58:01 AM4/1/21
to skia-d...@googlegroups.com
What is the value of colorType passed to MakeFromBackendRenderTarget? Does it work if you use kBGRA_8888_SkColorType?

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/skia-discuss/414258df-ffb0-4266-972f-e01ca2a6effan%40googlegroups.com.

Jim Van Verth

unread,
Apr 1, 2021, 10:55:18 AM4/1/21
to skia-discuss
I'm trying to get a sense of what you're doing here -- you're creating an offscreen surface that you want to render to, is that the idea? MTLPixelFormatBGRA8Unorm should be supported as a rendertarget format, so I'm not sure what would be going wrong.

As far as SkSurface::MakeFromCALayer, it defers grabbing the drawable texture as long as possible. The SkSurface created has a texture proxy, which doesn't get assigned a native texture (in this case, by grabbing it from the drawable) until we start the rendering process that needs it. You would need to create a new SkSurface for each frame. See tools/sk_app/MetalWindowContext.mm for an example of how to use it.



--

Jim Van Verth | Software Engineer | jvan...@google.com | 919-210-7664

Jim Van Verth

unread,
Apr 1, 2021, 10:58:22 AM4/1/21
to skia-discuss
Oh I see now. I think Brian has it right -- a mismatched colorType would cause the surface creation to fail.

Anmol Sud

unread,
Apr 6, 2021, 4:29:40 PM4/6/21
to skia-discuss
That was right. It was indeed a mismatch with ColorType. But if I create a surface with a texture and try to reuse it, I will still have to write a render pipeline to copy textures. I was trying to write a Blit function, but looks like BlitCommands don't work on drawable which have "FrameBufferOnly" set to True. 

As for using the surface created with drawable for every frame, is there a known performance difference between the two? Just curious.

Thanks.

Jim Van Verth

unread,
Apr 7, 2021, 4:42:38 PM4/7/21
to skia-discuss
That's right, you can't copy from a framebufferOnly rendertarget. However, you can set the CAMetalLayer's framebufferOnly member to NO and use it with Skia if you need to, though there might be some loss in performance.

As for the second question, I'm not sure what the two things you're referring to are. We have to create a new surface with each MTLDrawable because the texture handle we get back is different every time -- we can't just store two or three and cycle through them. So the performance is just what it is -- we can't do anything else. With other APIs it's possible to query the swapchain backbuffers, extract SkSurfaces from them and cache them, and then cycle through those.

Jarlen John

unread,
Mar 14, 2023, 8:35:39 AM3/14/23
to skia-discuss
Hi, I resolved this issue,  note that pixelformat of your created mtltexture must same as colorType of "MakeFromBackendRenderTarget..." method.

For example:
you want use "MTLPixelFormatBGRA8Unorm", first when you create your texture, you must designed it with "MakeFromBackendRenderTarget", then setup "const SkColorType colorType = kBGRA_8888_SkColorType;" as param of "SkSurface::MakeFromBackendRenderTarget".

hope it could help you and everyone seeing it. Thanks!

Reply all
Reply to author
Forward
0 new messages