Metal alpha channel oddity
I'm trying to render to a MTLTexture
within an MTKView
and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.
Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.
However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:
- I create my own texture ('clearTexture') with 0x00000000 pixel values
- This texture ends up the GPU (
MTLStorageModeManaged
) - I then pull that texture off the GPU ('testTexture')
- I grab that texture's pixel bytes
- What do I notice? Pixel values of 0x0000ffff (pure red)
I'm baffled.
Here's the setup:
CGSize textureSize = CGSizeMake(1920,1080);
// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;
// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];
// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];
// a bunch of other rendering (I leave 'testTexture' untouched)
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// I then check out the pixels
[self debugTexture:testTexture];
At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.
-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}
-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}
-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}
metal metalkit
add a comment |
I'm trying to render to a MTLTexture
within an MTKView
and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.
Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.
However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:
- I create my own texture ('clearTexture') with 0x00000000 pixel values
- This texture ends up the GPU (
MTLStorageModeManaged
) - I then pull that texture off the GPU ('testTexture')
- I grab that texture's pixel bytes
- What do I notice? Pixel values of 0x0000ffff (pure red)
I'm baffled.
Here's the setup:
CGSize textureSize = CGSizeMake(1920,1080);
// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;
// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];
// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];
// a bunch of other rendering (I leave 'testTexture' untouched)
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// I then check out the pixels
[self debugTexture:testTexture];
At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.
-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}
-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}
-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}
metal metalkit
I don't know if this representative of your actual code, but, in the first snippet, you declaremetalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?
– Ken Thomases
Jan 2 at 0:28
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43
add a comment |
I'm trying to render to a MTLTexture
within an MTKView
and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.
Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.
However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:
- I create my own texture ('clearTexture') with 0x00000000 pixel values
- This texture ends up the GPU (
MTLStorageModeManaged
) - I then pull that texture off the GPU ('testTexture')
- I grab that texture's pixel bytes
- What do I notice? Pixel values of 0x0000ffff (pure red)
I'm baffled.
Here's the setup:
CGSize textureSize = CGSizeMake(1920,1080);
// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;
// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];
// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];
// a bunch of other rendering (I leave 'testTexture' untouched)
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// I then check out the pixels
[self debugTexture:testTexture];
At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.
-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}
-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}
-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}
metal metalkit
I'm trying to render to a MTLTexture
within an MTKView
and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.
Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.
However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:
- I create my own texture ('clearTexture') with 0x00000000 pixel values
- This texture ends up the GPU (
MTLStorageModeManaged
) - I then pull that texture off the GPU ('testTexture')
- I grab that texture's pixel bytes
- What do I notice? Pixel values of 0x0000ffff (pure red)
I'm baffled.
Here's the setup:
CGSize textureSize = CGSizeMake(1920,1080);
// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;
// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];
// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];
// a bunch of other rendering (I leave 'testTexture' untouched)
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// I then check out the pixels
[self debugTexture:testTexture];
At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.
-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}
-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}
-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}
metal metalkit
metal metalkit
asked Jan 1 at 22:06
zzyzyzzyzy
543314
543314
I don't know if this representative of your actual code, but, in the first snippet, you declaremetalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?
– Ken Thomases
Jan 2 at 0:28
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43
add a comment |
I don't know if this representative of your actual code, but, in the first snippet, you declaremetalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?
– Ken Thomases
Jan 2 at 0:28
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43
I don't know if this representative of your actual code, but, in the first snippet, you declare
metalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?– Ken Thomases
Jan 2 at 0:28
I don't know if this representative of your actual code, but, in the first snippet, you declare
metalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?– Ken Thomases
Jan 2 at 0:28
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999314%2fmetal-alpha-channel-oddity%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999314%2fmetal-alpha-channel-oddity%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I don't know if this representative of your actual code, but, in the first snippet, you declare
metalTextureDescriptor
as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?– Ken Thomases
Jan 2 at 0:28
Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!
– zzyzy
Jan 2 at 4:44
Glad you found it. You can post an answer of your own, so people know it's been answered.
– Ken Thomases
Jan 2 at 5:43