SKShader.CreateLocalMatrix may or may not return the same object? #2652
Replies: 2 comments 4 replies
-
This is very interesting... Sometimes they reuse objects, but I did not think they did for SKShader... The docs on the member don't say anything: /**
* Return a shader that will apply the specified localMatrix to this shader.
* The specified matrix will be applied before any matrix associated with this shader.
*/ But I see the code does: sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
if (localMatrix.isIdentity()) {
return sk_ref_sp(const_cast<SkShader*>(this));
}
// ...
} I can modify the C++ code to always make a copy? |
Beta Was this translation helpful? Give feedback.
-
I think both the C# api wrapper and the C++ code should return the same object, but that object should have an increased ref count? if (localMatrix.isIdentity()) {
return sk_ref_sp(const_cast<SkShader*>(this));
} Shuldn't this increase the ref count to 'this' by one before it returns the same ref counted pointer? I can't see where it's going wrong then. But it definitely changed behavior since 1.X Here is the behavior in e.g. v1.60.1 (I Didn't actually look at the ref counts in this version because I didn't find SkiaApi.sk_refcnt_get_ref_count but the object using (SKShader shader1 = SKShader.CreateColor(new SKColor())) // shader1 has ref count=1
{
using (SKShader temp = SKShader.CreateLocalMatrix(shader1, mat)) // Temp has the same Handle but with ref count 2
{
// use temp wrapper here
} // temp is disposed which decrements the ref count on its handle to 1, so the handle is still not IntPtr.Zero
// keep using shader1 here. This works.
} // shader1 is disposed which decrements the ref count to 0 and the Handle becomes IntPtr.Zero here. Behavior in v2.88: using (SKShader shader1 = SKShader.CreateColor(new SKColor())) // shader1 has ref count=1
{
using (SKShader temp = SKShader.CreateLocalMatrix(shader1, mat)) // Temp has the same Handle, and ref count=1 (!)
{
// use temp wrapper here
} // temp is disposed, ref count becomes 0, so the handle becomes IntPtr.Zero for both temp and shader1
// keep using shader1 here. This will fail.
} In 2.88 I could actually check the ref counts in the above example simply by adding a watch statement for SkiaApi.sk_refcnt_get_ref_count to confirm that the ref count is never 2. Update: Stepping through it I can see that the createLocalMatrix does bump the ref count to 2, but that the managed wrapper does an unref() before returning. That happens in GetOrAddObject() which is called with unrefExisting=true
I used this test case [Fact]
public void TestCreateLocalMatrixRefCount()
{
SKShader s1 = SKShader.CreateColor(new SKColor(255, 0, 0, 128));
Assert.Equal(1, SkiaApi.sk_refcnt_get_ref_count(s1.Handle));
SKShader s2 = s1.WithLocalMatrix(SKMatrix.CreateIdentity());
Assert.True(ReferenceEquals(s1, s2));
Assert.Equal(2, SkiaApi.sk_refcnt_get_ref_count(s1.Handle));
s2.Dispose();
Assert.Equal(1, SkiaApi.sk_refcnt_get_ref_count(s1.Handle));
Assert.NotEqual(IntPtr.Zero, s1.Handle);
s1.Dispose();
Assert.Equal(IntPtr.Zero, s1.Handle);
} |
Beta Was this translation helpful? Give feedback.
-
I'm trying to wrap my head around how the managed wrappers for objects like shaders work, when there are functions on the SKShader type that might return the SAME object. Example: the return value of SKShader.CreateLocalMatrix may or may not be the argument object. If the matrix is an identity then the same object is returned.
But this behavior makes it really hard to work in the case of a general matrix. Say you have an image in a shader that you want to retain (not dispose) and you wrap it in an outer shader with a matrix. What then have you created so that you need to dispose it? "It depends"?
It feels like the API becomes kind of clumsy for a C# IDisposable when a method can sometimes return its argument and some times not? Have I misunderstood something?
Is this optimization to make the SKShader.Create..(...) methods potentially return new OR the same object new behavior in 2.X?
Beta Was this translation helpful? Give feedback.
All reactions